1 line
6.5 MiB
Plaintext
1 line
6.5 MiB
Plaintext
{"repo": "alessiobacin/llmproxy", "files": {"tests/cli.test.js": {"8c29c098b9e92b82f038f604a765a9c2212ebea4": {"t": 1785746673.4360309, "n": 232385, "h": "92c908848b2a9c5b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--vision\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:test default runs full text inference for every configured provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-default-text-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-or-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"OpenRouter\" });\n\n let capturedBody = null;\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async (url, init) => {\n capturedBody = init?.body ? JSON.parse(init.body) : null;\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n async text() { return JSON.stringify({ type: \"message\", content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }] }); },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Test inferenza provider/i);\n assert.match(stdout.toString(), /OpenRouter \\(deepseek-v4-flash\\)/i);\n assert.match(stdout.toString(), /✅ PASS/i);\n // Verify text-only message (no image)\n assert.ok(capturedBody, \"should have sent a request body\");\n assert.equal(capturedBody.messages[0].content.length, 1, \"should send only text content, no image\");\n assert.equal(capturedBody.messages[0].content[0].type, \"text\");\n});\n\ntest(\"provider:test --all-proxies runs real inference across all provider/proxy pairs with grouped output\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n vision: true,\n }, { name: \"Qwen\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n proxyStore.addProxy(\"http://proxy:two@37.27.55.17:7064/\");\n\n const seenBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n seenBodies.push(body);\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: `req-${body.provider}-${seenBodies.length}`,\n success: true,\n finalProvider: body.provider,\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: body.provider, status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(seenBodies.length, 4);\n seenBodies.forEach((body) => {\n assert.match(body.messages[0].content[0].text, /^Rispondi solo: llmproxy-proxy-test-/);\n assert.equal(body.messages[0].content.length, 1);\n });\n assert.match(stdout.toString(), /Test di inferenza provider × proxy/);\n assert.match(stdout.toString(), /DeepSeek \\(deepseek-v4-flash\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n 37\\.27\\.55\\.17\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n\\nQwen \\(qwen3\\.7-plus\\)/);\n assert.doesNotMatch(stdout.toString(), /Visione|visione|immagine/);\n assert.match(stdout.toString(), /Risultati: 4 pass, 0 fail, 0 skip/);\n});\n\ntest(\"provider:test --all-proxies accepts canonical finalProvider for instance-scoped providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-opencode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n }, { name: \"opencode-alessio\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-instance\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: \"opencode\", status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio \\(deepseek-v4-flash-free\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash-free/i);\n assert.doesNotMatch(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 1 pass, 0 fail, 0 skip/i);\n});\n\ntest(\"provider:test --all-proxies returns a failing exit code when at least one provider-proxy pair fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-exit-code-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 0 pass, 1 fail, 0 skip/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart force-recreates the Docker runtime even when the container is already running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"recreated\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\")), true);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\", \"--all\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\", \"--all\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_SHORT_ANSWER\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /può essere impostata soltanto nello scope 'service'/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /├─ Providers/);\n assert.match(stdout.toString(), /│ 1 qwen\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek\\s+1\\s+60\\s+50\\s+10 │/);\n assert.match(stdout.toString(), /├─ Models/);\n assert.match(stdout.toString(), /│ 1 qwen3\\.7-max\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek-v4-pro\\s+1\\s+60\\s+50\\s+10 │/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- Release 0\\.2\\.60\\./);\n assert.match(stdout.toString(), /- Embed note di rilascio correnti nel pacchetto installato per compatibilità first-update\\./);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "ed9622047c71c0a3e03a83986cb2f510fc26fe17": {"t": 1785766740.0662181, "n": 232385, "h": "92c908848b2a9c5b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--vision\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:test default runs full text inference for every configured provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-default-text-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-or-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"OpenRouter\" });\n\n let capturedBody = null;\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async (url, init) => {\n capturedBody = init?.body ? JSON.parse(init.body) : null;\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n async text() { return JSON.stringify({ type: \"message\", content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }] }); },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Test inferenza provider/i);\n assert.match(stdout.toString(), /OpenRouter \\(deepseek-v4-flash\\)/i);\n assert.match(stdout.toString(), /✅ PASS/i);\n // Verify text-only message (no image)\n assert.ok(capturedBody, \"should have sent a request body\");\n assert.equal(capturedBody.messages[0].content.length, 1, \"should send only text content, no image\");\n assert.equal(capturedBody.messages[0].content[0].type, \"text\");\n});\n\ntest(\"provider:test --all-proxies runs real inference across all provider/proxy pairs with grouped output\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n vision: true,\n }, { name: \"Qwen\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n proxyStore.addProxy(\"http://proxy:two@37.27.55.17:7064/\");\n\n const seenBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n seenBodies.push(body);\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: `req-${body.provider}-${seenBodies.length}`,\n success: true,\n finalProvider: body.provider,\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: body.provider, status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(seenBodies.length, 4);\n seenBodies.forEach((body) => {\n assert.match(body.messages[0].content[0].text, /^Rispondi solo: llmproxy-proxy-test-/);\n assert.equal(body.messages[0].content.length, 1);\n });\n assert.match(stdout.toString(), /Test di inferenza provider × proxy/);\n assert.match(stdout.toString(), /DeepSeek \\(deepseek-v4-flash\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n 37\\.27\\.55\\.17\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n\\nQwen \\(qwen3\\.7-plus\\)/);\n assert.doesNotMatch(stdout.toString(), /Visione|visione|immagine/);\n assert.match(stdout.toString(), /Risultati: 4 pass, 0 fail, 0 skip/);\n});\n\ntest(\"provider:test --all-proxies accepts canonical finalProvider for instance-scoped providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-opencode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n }, { name: \"opencode-alessio\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-instance\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: \"opencode\", status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio \\(deepseek-v4-flash-free\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash-free/i);\n assert.doesNotMatch(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 1 pass, 0 fail, 0 skip/i);\n});\n\ntest(\"provider:test --all-proxies returns a failing exit code when at least one provider-proxy pair fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-exit-code-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 0 pass, 1 fail, 0 skip/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart force-recreates the Docker runtime even when the container is already running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"recreated\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\")), true);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\", \"--all\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\", \"--all\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_SHORT_ANSWER\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /può essere impostata soltanto nello scope 'service'/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /├─ Providers/);\n assert.match(stdout.toString(), /│ 1 qwen\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek\\s+1\\s+60\\s+50\\s+10 │/);\n assert.match(stdout.toString(), /├─ Models/);\n assert.match(stdout.toString(), /│ 1 qwen3\\.7-max\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek-v4-pro\\s+1\\s+60\\s+50\\s+10 │/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- Release 0\\.2\\.60\\./);\n assert.match(stdout.toString(), /- Embed note di rilascio correnti nel pacchetto installato per compatibilità first-update\\./);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "438fdbe430a16f53377382c4cc45ec5171875711": {"t": 1785766742.1859396, "n": 232367, "h": "fd6cb0ecb27f9bfd", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--vision\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:test default runs full text inference for every configured provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-default-text-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-or-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"OpenRouter\" });\n\n let capturedBody = null;\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async (url, init) => {\n capturedBody = init?.body ? JSON.parse(init.body) : null;\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n async text() { return JSON.stringify({ type: \"message\", content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }] }); },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Test inferenza provider/i);\n assert.match(stdout.toString(), /OpenRouter \\(deepseek-v4-flash\\)/i);\n assert.match(stdout.toString(), /✅ PASS/i);\n // Verify text-only message (no image)\n assert.ok(capturedBody, \"should have sent a request body\");\n assert.equal(capturedBody.messages[0].content.length, 1, \"should send only text content, no image\");\n assert.equal(capturedBody.messages[0].content[0].type, \"text\");\n});\n\ntest(\"provider:test --all-proxies runs real inference across all provider/proxy pairs with grouped output\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n vision: true,\n }, { name: \"Qwen\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n proxyStore.addProxy(\"http://proxy:two@37.27.55.17:7064/\");\n\n const seenBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n seenBodies.push(body);\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: `req-${body.provider}-${seenBodies.length}`,\n success: true,\n finalProvider: body.provider,\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: body.provider, status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(seenBodies.length, 4);\n seenBodies.forEach((body) => {\n assert.match(body.messages[0].content[0].text, /^Rispondi solo: llmproxy-proxy-test-/);\n assert.equal(body.messages[0].content.length, 1);\n });\n assert.match(stdout.toString(), /Test di inferenza provider × proxy/);\n assert.match(stdout.toString(), /DeepSeek \\(deepseek-v4-flash\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n 37\\.27\\.55\\.17\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n\\nQwen \\(qwen3\\.7-plus\\)/);\n assert.doesNotMatch(stdout.toString(), /Visione|visione|immagine/);\n assert.match(stdout.toString(), /Risultati: 4 pass, 0 fail, 0 skip/);\n});\n\ntest(\"provider:test --all-proxies accepts canonical finalProvider for instance-scoped providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-opencode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n }, { name: \"opencode-alessio\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-instance\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: \"opencode\", status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio \\(deepseek-v4-flash-free\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash-free/i);\n assert.doesNotMatch(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 1 pass, 0 fail, 0 skip/i);\n});\n\ntest(\"provider:test --all-proxies returns a failing exit code when at least one provider-proxy pair fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-exit-code-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 0 pass, 1 fail, 0 skip/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart force-recreates the Docker runtime even when the container is already running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"recreated\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\")), true);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_SHORT_ANSWER\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /può essere impostata soltanto nello scope 'service'/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /├─ Providers/);\n assert.match(stdout.toString(), /│ 1 qwen\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek\\s+1\\s+60\\s+50\\s+10 │/);\n assert.match(stdout.toString(), /├─ Models/);\n assert.match(stdout.toString(), /│ 1 qwen3\\.7-max\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek-v4-pro\\s+1\\s+60\\s+50\\s+10 │/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- Release 0\\.2\\.60\\./);\n assert.match(stdout.toString(), /- Embed note di rilascio correnti nel pacchetto installato per compatibilità first-update\\./);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "f937a6d2ac930e3c037e6c8bcede2cd7c614fc36": {"t": 1785766744.0295823, "n": 230536, "h": "db0064621f6fb064", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:test --all-proxies runs real inference across all provider/proxy pairs with grouped output\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n vision: true,\n }, { name: \"Qwen\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n proxyStore.addProxy(\"http://proxy:two@37.27.55.17:7064/\");\n\n const seenBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n seenBodies.push(body);\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: `req-${body.provider}-${seenBodies.length}`,\n success: true,\n finalProvider: body.provider,\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: body.provider, status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(seenBodies.length, 4);\n seenBodies.forEach((body) => {\n assert.match(body.messages[0].content[0].text, /^Rispondi solo: llmproxy-proxy-test-/);\n assert.equal(body.messages[0].content.length, 1);\n });\n assert.match(stdout.toString(), /Test di inferenza provider × proxy/);\n assert.match(stdout.toString(), /DeepSeek \\(deepseek-v4-flash\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n 37\\.27\\.55\\.17\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n\\nQwen \\(qwen3\\.7-plus\\)/);\n assert.doesNotMatch(stdout.toString(), /Visione|visione|immagine/);\n assert.match(stdout.toString(), /Risultati: 4 pass, 0 fail, 0 skip/);\n});\n\ntest(\"provider:test --all-proxies accepts canonical finalProvider for instance-scoped providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-opencode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n }, { name: \"opencode-alessio\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-instance\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: \"opencode\", status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio \\(deepseek-v4-flash-free\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash-free/i);\n assert.doesNotMatch(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 1 pass, 0 fail, 0 skip/i);\n});\n\ntest(\"provider:test --all-proxies returns a failing exit code when at least one provider-proxy pair fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-exit-code-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 0 pass, 1 fail, 0 skip/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart force-recreates the Docker runtime even when the container is already running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"recreated\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\")), true);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_SHORT_ANSWER\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /può essere impostata soltanto nello scope 'service'/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /├─ Providers/);\n assert.match(stdout.toString(), /│ 1 qwen\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek\\s+1\\s+60\\s+50\\s+10 │/);\n assert.match(stdout.toString(), /├─ Models/);\n assert.match(stdout.toString(), /│ 1 qwen3\\.7-max\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek-v4-pro\\s+1\\s+60\\s+50\\s+10 │/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- Release 0\\.2\\.60\\./);\n assert.match(stdout.toString(), /- Embed note di rilascio correnti nel pacchetto installato per compatibilità first-update\\./);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "2d20494615007a629b32713b522aa0ddfc5ef607": {"t": 1785766746.0649047, "n": 228489, "h": "4d10aa799b74013b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:test --all-proxies runs real inference across all provider/proxy pairs with grouped output\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n vision: true,\n }, { name: \"Qwen\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n proxyStore.addProxy(\"http://proxy:two@37.27.55.17:7064/\");\n\n const seenBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n seenBodies.push(body);\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: `req-${body.provider}-${seenBodies.length}`,\n success: true,\n finalProvider: body.provider,\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: body.provider, status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(seenBodies.length, 4);\n seenBodies.forEach((body) => {\n assert.match(body.messages[0].content[0].text, /^Rispondi solo: llmproxy-proxy-test-/);\n assert.equal(body.messages[0].content.length, 1);\n });\n assert.match(stdout.toString(), /Test di inferenza provider × proxy/);\n assert.match(stdout.toString(), /DeepSeek \\(deepseek-v4-flash\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n 37\\.27\\.55\\.17\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n\\nQwen \\(qwen3\\.7-plus\\)/);\n assert.doesNotMatch(stdout.toString(), /Visione|visione|immagine/);\n assert.match(stdout.toString(), /Risultati: 4 pass, 0 fail, 0 skip/);\n});\n\ntest(\"provider:test --all-proxies accepts canonical finalProvider for instance-scoped providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-opencode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n }, { name: \"opencode-alessio\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-instance\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: \"opencode\", status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio \\(deepseek-v4-flash-free\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash-free/i);\n assert.doesNotMatch(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 1 pass, 0 fail, 0 skip/i);\n});\n\ntest(\"provider:test --all-proxies returns a failing exit code when at least one provider-proxy pair fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-exit-code-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 0 pass, 1 fail, 0 skip/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_SHORT_ANSWER\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /può essere impostata soltanto nello scope 'service'/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /├─ Providers/);\n assert.match(stdout.toString(), /│ 1 qwen\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek\\s+1\\s+60\\s+50\\s+10 │/);\n assert.match(stdout.toString(), /├─ Models/);\n assert.match(stdout.toString(), /│ 1 qwen3\\.7-max\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek-v4-pro\\s+1\\s+60\\s+50\\s+10 │/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- Release 0\\.2\\.60\\./);\n assert.match(stdout.toString(), /- Embed note di rilascio correnti nel pacchetto installato per compatibilità first-update\\./);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "8ce338404ea288515ab1d96d1486d2790747aca5": {"t": 1785766747.7880194, "n": 222058, "h": "77f8203dac9cc740", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "7386e60d789110e7e7b9466a61b28df166e94e15": {"t": 1785766749.6532273, "n": 220735, "h": "ffc67dd5cec8ec18", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "510fbe499bc9c6040e2f6fe6d646562e0a51b227": {"t": 1785766751.6953924, "n": 218514, "h": "e868b4adc012c24b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "a1ab3a2f48b1c04f8e8c38b9aa22aeb5e333ca29": {"t": 1785766754.1710377, "n": 218364, "h": "14a920bc465c4a6b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "ab7da61d555e3358c029ac7743fa611aa21dada5": {"t": 1785766755.8938143, "n": 216453, "h": "3716907fbe4b8a25", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "4ebdcded9879dbb7fb80668183e2e2d313d11e0b": {"t": 1785766757.9720793, "n": 212650, "h": "ee6215bd62a3422a", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "9d7d48cafc97dd3ce905f3f7119d7b149401cb1c": {"t": 1785766759.7420805, "n": 211658, "h": "563feb70880d7c2a", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "e16d94b31ee1b37084a8b75f60881bfd553538c7": {"t": 1785766762.296522, "n": 211620, "h": "79812e07d30fa26d", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /llmp service:stop/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "f0b4cd4e920a780873a532fb39d8674c573382e2": {"t": 1785766764.1512241, "n": 207206, "h": "f206ff4f4d16fc1b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /llmp service:stop/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "89b3696a9909288c0a940b45de6c233f2c265ef9": {"t": 1785766766.3803446, "n": 206435, "h": "e7adf6d86a1d343e", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /llmp service:stop/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "5bf933a06861f75141490bd3e0b2ac261fe871ab": {"t": 1785766768.229102, "n": 204890, "h": "00cab15b2141085c", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "09c4ac8b1a2ea39e8f9f3a43993655b0c7556b19": {"t": 1785766770.0007455, "n": 204338, "h": "2c55f70157fcb3b5", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "ac3e7d8a90768f8f997e2781d41e4bf26454c690": {"t": 1785766772.2934947, "n": 204183, "h": "fbe5af30613fc8de", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "40edbc06c0ce69c73eb7c5b35ac42e1f22670a35": {"t": 1785766774.086019, "n": 204011, "h": "82df7a9d79fdac30", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /git clone --depth=1 .* \\*> \\$null/);\n assert.match(windowsScriptText, /npm install -g --force \"\\$packageFile\" \\*> \\$null/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate \\*> \\$null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "efd146aede448c14639dd88f79075e5408a7cfa6": {"t": 1785766776.1224527, "n": 203771, "h": "d04612543f6c7b4c", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "be0c1abf69e997e7295625a5821d2955f29d6af5": {"t": 1785766778.1656847, "n": 203494, "h": "0106e662cdfd282c", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "3cdba1d55b0436b454a881b2cae7104a4152592a": {"t": 1785766780.0739765, "n": 203575, "h": "361e157410e7efa0", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"npm.cmd\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm.cmd\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"npm.cmd\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "4e017e26b1f6f57f5be1ee874d51fb259785f315": {"t": 1785766782.1617584, "n": 201672, "h": "50dbce007cdc3ce7", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "b09602ca375ce92300afc4c81afa219213fd9c60": {"t": 1785766783.957811, "n": 197951, "h": "5f2d80b8916fed86", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "954604cc2c84de208568b4edee245da96e7250c0": {"t": 1785766785.7896273, "n": 192244, "h": "d9cd08679691b687", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "a57eceab443ded1579506dfe229caec5fe600ed1": {"t": 1785766787.5675063, "n": 190680, "h": "83ebf87dd7fb052a", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "ed3f9978fc0be4654d5e99b694ebb7fdac1360c4": {"t": 1785766789.7208056, "n": 187886, "h": "2fed38163a276585", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\(which -a llmproxy 2>\\/dev\\/null \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "522fffe02eb431af60caa0f297c244cf855ea29f": {"t": 1785766792.2651405, "n": 185528, "h": "7ab2e5ed1297fbd5", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\(which -a llmproxy 2>\\/dev\\/null \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "33b3ffbc0dcfeed847c28b8457a54ff7d653bb32": {"t": 1785766794.1026597, "n": 184196, "h": "e6d5f5ecac3b6fe5", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.06\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.06:\\n- release 0.3.06\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.06/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\(which -a llmproxy 2>\\/dev\\/null \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "fcbead336ab2d22ba7a03155786c6424bd5d9489": {"t": 1785766797.3206463, "n": 182237, "h": "218d60bf8843eec0", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.05\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.05:\\n- release 0.3.05\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.05/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\(which -a llmproxy 2>\\/dev\\/null \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "73acf32090c8718eb5806e8228402a0f0511427a": {"t": 1785766802.1227012, "n": 180553, "h": "dce8adf16ea522d8", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\(which -a llmproxy 2>\\/dev\\/null \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}}}} |