{"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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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"}, "87288e7c0ecc339242cf043f0e5f8e4f91ce34ce": {"t": 1786067860.9110973, "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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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"}, "a46f243b015da954b06ad4ebb34ee9f6a5af8cdb": {"t": 1786068486.9441342, "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 /);\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, \"\", \"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 /);\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 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 \\]/);\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 /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, \"\");\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, \"\");\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"}}, "lib/cli.js": {"a46f243b015da954b06ad4ebb34ee9f6a5af8cdb": {"t": 1786068492.6652336, "n": 276192, "h": "0f285e0a4599aef6", "c": "const fs = require(\"node:fs\");\nconst crypto = require(\"node:crypto\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\nconst { spawn, spawnSync } = require(\"node:child_process\");\n\n// --- Spinner minimale (solo TTY) per segnalare operazioni in corso ---\nfunction startSpinner(message) {\n const frames = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\n let i = 0;\n process.stdout.write(`\\x1b[36m${frames[0]} ${message}\\x1b[0m`);\n const timer = setInterval(() => {\n i = (i + 1) % frames.length;\n process.stdout.write(`\\r\\x1b[36m${frames[i]} ${message}\\x1b[0m`);\n }, 100);\n return { timer, message };\n}\nfunction stopSpinner(spinner) {\n if (!spinner) return;\n clearInterval(spinner.timer);\n process.stdout.write(\"\\r\\x1b[2K\\r\");\n}\nconst { createPaths, ensureRuntimeDirs, getDockerHostProjectsRoot } = require(\"./paths\");\nconst { createTokenStore } = require(\"./token-store\");\nconst { createProviderRegistry } = require(\"./provider-registry\");\nconst { createCopilotModelCatalogStore, resolveAvailableCopilotModels } = require(\"./copilot-models\");\nconst { startDeviceFlow, pollForToken } = require(\"./copilot-auth\");\nconst { createServiceManager } = require(\"./service-manager\");\nconst { startServer } = require(\"./app\");\nconst { getAvailableModels, mapModel, DEFAULT_COPILOT_MODEL } = require(\"./openai-translate\");\nconst { probeApiKeyProviderModel, normalizeQwenEndpointVariant, parseProviderModelPreferences, makeProxyFetch } = require(\"./copilot-proxy\");\nconst {\n loadRuntimeEnv,\n normalizeRuntimeProfile,\n resolveProxyHostPort,\n resolveRuntimeProfile,\n resolveServiceUrlForProfile,\n} = require(\"./runtime-env\");\nconst { createJsonlMeteringSink } = require(\"./metering\");\nconst { resolveClaudeProjectSettings } = require(\"./project-context\");\nconst { resolveDockerComposeCommand, runDockerCompose } = require(\"./docker-compose\");\nconst { listPortListeners } = require(\"./port-guard\");\nconst { createProviderReordering, readReorderingStore, buildDefaultProbeFn, fetchSpeedMetric, fetchBenchmarkMetric, DEFAULT_REORDERING_FILE } = require(\"./provider-reordering\");\nconst {\n readJsonResponseSafe,\n normalizeCloudPriceModelCandidates,\n extractBenchmarkCodingScore,\n fetchCodingScore,\n mapProviderToCloudPriceIds,\n fetchModelPricing,\n pickCurrentPricingOption,\n} = require(\"./cloudprice-client\");\nconst {\n inferScopeFromKey,\n getGlobalClaudeSettingsFile,\n hasLocalClaudeFolder,\n listConfigSpecs,\n listScopeValues,\n getScopeValue,\n migrateManagedConfig,\n readServiceConfig,\n setScopeValue,\n unsetScopeValue,\n writeServiceConfig,\n} = require(\"./configuration\");\nfunction buildProxyAgentUrl(proxyUrlRaw, proxyApiKey) {\n if (!proxyUrlRaw && !proxyApiKey) return \"\";\n const normalizedRaw = String(proxyUrlRaw || \"\").trim();\n const withScheme = normalizedRaw && !/^[a-z][a-z0-9+.-]*:\\/\\//i.test(normalizedRaw)\n ? `http://${normalizedRaw}`\n : normalizedRaw;\n let parsed = null;\n try {\n parsed = withScheme ? new URL(withScheme) : null;\n } catch {\n parsed = null;\n }\n const key = (proxyApiKey || \"\").trim();\n if (parsed?.hostname) {\n const protocol = parsed.protocol || \"http:\";\n let auth = \"\";\n if (key) {\n const user = parsed.username || \"proxy\";\n auth = `${encodeURIComponent(user)}:${encodeURIComponent(key)}@`;\n } else if (parsed.username) {\n const pw = parsed.password ? encodeURIComponent(parsed.password) : \"\";\n auth = `${encodeURIComponent(parsed.username)}:${pw}@`;\n }\n const host = parsed.hostname.includes(\":\") ? `[${parsed.hostname}]` : parsed.hostname;\n const port = parsed.port || \"7064\";\n return `${protocol}//${auth}${host}:${port}`;\n }\n return \"\";\n}\n\nconst AVAILABLE_PROVIDER_SPECS = [\n { id: \"copilot\", auth: \"oauth\", displayName: \"GitHub Copilot\", aliases: [] },\n { id: \"openrouter\", auth: \"api_key\", displayName: \"OpenRouter\", aliases: [] },\n { id: \"zai\", auth: \"api_key\", displayName: \"Z.AI\", aliases: [\"z.ai\"] },\n { id: \"kimi\", auth: \"api_key\", displayName: \"Kimi (Moonshot)\", aliases: [] },\n { id: \"qwen\", auth: \"api_key\", displayName: \"Qwen (DashScope)\", aliases: [] },\n { id: \"opencode\", auth: \"api_key\", displayName: \"OpenCode Zen\", aliases: [\"zen\"] },\n { id: \"opencode-go\", auth: \"api_key\", displayName: \"OpenCode Go\", aliases: [\"go\", \"opencodego\"] },\n { id: \"openai\", auth: \"api_key\", displayName: \"OpenAI\", aliases: [] },\n { id: \"anthropic\", auth: \"api_key\", displayName: \"Anthropic\", aliases: [] },\n { id: \"deepseek\", auth: \"api_key\", displayName: \"DeepSeek\", aliases: [] },\n { id: \"groq\", auth: \"api_key\", displayName: \"Groq\", aliases: [] },\n { id: \"mistral\", auth: \"api_key\", displayName: \"Mistral\", aliases: [] },\n { id: \"xai\", auth: \"api_key\", displayName: \"xAI\", aliases: [] },\n { id: \"perplexity\", auth: \"api_key\", displayName: \"Perplexity\", aliases: [] },\n { id: \"together\", auth: \"api_key\", displayName: \"Together\", aliases: [] },\n { id: \"fireworks\", auth: \"api_key\", displayName: \"Fireworks\", aliases: [] },\n { id: \"commandcode\", auth: \"api_key\", displayName: \"Command Code\", aliases: [] },\n { id: \"nvidia\", auth: \"api_key\", displayName: \"NVIDIA\", aliases: [] },\n { id: \"vercel-ai-gateway\", auth: \"api_key\", displayName: \"Vercel AI Gateway\", aliases: [\"ai-gateway\", \"vercel\"] },\n { id: \"meta\", auth: \"api_key\", displayName: \"Meta AI\", aliases: [] },\n];\n\nconst KNOWN_PROVIDERS = AVAILABLE_PROVIDER_SPECS.reduce((acc, provider) => {\n const entry = { auth: provider.auth, displayName: provider.displayName };\n acc[provider.id] = entry;\n for (const alias of provider.aliases) acc[alias] = entry;\n return acc;\n}, {});\n\nconst SHORT_COMMAND_ALIASES = {\n // Comandi senza gruppo\n h: \"help\",\n su: \"setup\",\n v: \"version\",\n rn: \"release-notes\",\n up: \"update\",\n un: \"uninstall\",\n in: \"install\",\n li: \"login\",\n lo: \"logout\",\n sto: \"stop\",\n st: \"status\",\n sa: \"stats\",\n lg: \"logs\",\n t: \"test\",\n // Gruppo provider\n \"p:a\": \"provider:add\",\n \"p:k\": \"provider:key\",\n \"p:av\": \"provider:available\",\n \"p:l\": \"provider:list\",\n \"p:t\": \"provider:test\",\n \"p:s\": \"provider:status\",\n \"p:u\": \"provider:usage\",\n \"p:o\": \"provider:order\",\n \"p:ro\": \"provider:reorder\",\n \"p:up\": \"provider:update\",\n \"p:rn\": \"provider:rename\",\n \"p:rm\": \"provider:remove\",\n // Gruppo config\n \"c:l\": \"config:list\",\n \"c:g\": \"config:get\",\n \"c:s\": \"config:set\",\n \"c:u\": \"config:unset\",\n // Gruppo service\n \"sv:sta\": \"service:start\",\n \"sv:sto\": \"service:stop\",\n \"sv:r\": \"service:restart\",\n \"sv:rt\": \"service:runtime\",\n // Gruppo model/models\n \"m:l\": \"models:list\",\n \"m:s\": \"model:set\",\n // Gruppo claude\n \"cc:s\": \"claude:setup\",\n // Gruppo pi\n \"pi:s\": \"pi:setup\",\n // Gruppo vscode\n \"vsc:cs\": \"vscode-chat:setup\",\n \"vsc:cc\": \"vscode-claude:setup\",\n // Gruppo stats\n \"sa:r\": \"stats:reset\",\n // Gruppo install\n \"in:p\": \"install:persistent\",\n \"in:it\": \"install:persistent-it\",\n \"in:en\": \"install:persistent-en\",\n // Gruppo proxy\n \"px:a\": \"proxy:add\",\n \"px:l\": \"proxy:list\",\n \"px:rm\": \"proxy:remove\",\n \"px:ro\": \"proxy:reorder\",\n \"px:t\": \"proxy:test\",\n};\n\nconst INSTALL_LOCALE_FILE = \"install-locale.txt\";\n\nconst REVERSE_SHORT_ALIASES = Object.fromEntries(\n Object.entries(SHORT_COMMAND_ALIASES).map(([short, full]) => [full, short]),\n);\n\nconst RELEASE_NOTES = {};\n\nfunction normalizeLocale(value) {\n return String(value || \"\").trim().toLowerCase() === \"en\" ? \"en\" : \"it\";\n}\n\nfunction getInstallLocaleFilePath(paths) {\n return path.join(paths.dataRoot, INSTALL_LOCALE_FILE);\n}\n\nfunction persistInstallLocale(paths, locale) {\n try {\n fs.writeFileSync(getInstallLocaleFilePath(paths), `${normalizeLocale(locale)}\\n`, \"utf8\");\n } catch {\n // best effort\n }\n}\n\nfunction readPersistedInstallLocale(paths) {\n try {\n const raw = fs.readFileSync(getInstallLocaleFilePath(paths), \"utf8\").trim();\n return normalizeLocale(raw);\n } catch {\n return \"it\";\n }\n}\n\nfunction resolveOutputLocale({ env, paths }) {\n const envLocale = String(env?.LLMPROXY_LOCALE || \"\").trim().toLowerCase();\n if (envLocale === \"it\" || envLocale === \"en\") return envLocale;\n return readPersistedInstallLocale(paths);\n}\n\nfunction normalizeKnownProviderId(rawProviderId) {\n const normalized = String(rawProviderId || \"\").trim().toLowerCase();\n if (normalized === \"z.ai\") return \"zai\";\n if (normalized === \"zen\" || normalized === \"opencode\") return \"opencode\";\n if (normalized === \"go\" || normalized === \"opencodego\" || normalized === \"opencode-go\") return \"opencode-go\";\n return normalized;\n}\n\nfunction resolveApiKeyProviderKind(rawInstanceId, rawProviderFlag) {\n const explicitProviderKind = normalizeKnownProviderId(rawProviderFlag);\n if (explicitProviderKind && KNOWN_PROVIDERS[explicitProviderKind]?.auth === \"api_key\") {\n return explicitProviderKind;\n }\n\n const normalizedInstanceId = normalizeKnownProviderId(rawInstanceId);\n if (normalizedInstanceId && KNOWN_PROVIDERS[normalizedInstanceId]?.auth === \"api_key\") {\n return normalizedInstanceId;\n }\n\n const knownApiKeyIds = AVAILABLE_PROVIDER_SPECS\n .filter((provider) => provider.auth === \"api_key\")\n .flatMap((provider) => [provider.id, ...(provider.aliases || [])])\n .map((providerId) => normalizeKnownProviderId(providerId))\n .filter(Boolean)\n .sort((left, right) => right.length - left.length);\n\n for (const providerId of knownApiKeyIds) {\n if (normalizedInstanceId === providerId || normalizedInstanceId.startsWith(`${providerId}-`)) {\n return providerId;\n }\n }\n\n return \"\";\n}\n\nfunction getKnownProvider(rawProviderId) {\n const id = normalizeKnownProviderId(rawProviderId);\n if (KNOWN_PROVIDERS[id]) {\n return { id, ...KNOWN_PROVIDERS[id] };\n }\n // Backward compatibility: arbitrary ids are treated as Copilot aliases.\n return { id, auth: \"oauth\", displayName: id || \"GitHub Copilot\" };\n}\n\nfunction listAvailableProviders() {\n return AVAILABLE_PROVIDER_SPECS.map((provider) => ({ ...provider, aliases: provider.aliases.slice() }));\n}\n\nfunction formatAvailableProviders(providers = listAvailableProviders()) {\n return providers\n .map((provider, index) => {\n const aliasSuffix = provider.aliases.length > 0 ? ` aliases=${provider.aliases.join(\", \")}` : \"\";\n return `${index + 1}. ${provider.id} (${provider.displayName}) auth=${provider.auth}${aliasSuffix}`;\n })\n .join(\"\\n\");\n}\n\nfunction formatKnownProvidersInline(providers = listAvailableProviders()) {\n return providers\n .map((provider) => (provider.aliases.length > 0 ? `${provider.aliases[0]} (${provider.id})` : provider.id))\n .join(\", \");\n}\n\nfunction parseArgs(argv) {\n const tokens = Array.isArray(argv) ? argv.slice(2) : [];\n const args = [];\n const flags = {};\n\n if (tokens.length > 0 && (tokens[0] === \"--help\" || tokens[0] === \"-h\")) {\n return {\n command: \"help\",\n subcommand: tokens[1] || \"\",\n args: tokens.slice(1),\n flags: {},\n follow: false,\n };\n }\n\n if (tokens.length > 0 && (tokens[0] === \"--version\" || tokens[0] === \"-v\")) {\n return {\n command: \"version\",\n subcommand: \"\",\n args: [],\n flags: {},\n follow: false,\n };\n }\n\n for (let index = 0; index < tokens.length; index += 1) {\n const token = tokens[index];\n if (token.startsWith(\"-\") && !token.startsWith(\"--\")) {\n const shortFlags = token.slice(1).trim();\n if (shortFlags) {\n // Se il token dopo il singolo dash contiene un hyphen, è un long flag\n // scritto con un dash solo per errore (es. -all-providers → --all-providers)\n if (shortFlags.includes(\"-\")) {\n flags[shortFlags] = true;\n continue;\n }\n for (const shortFlag of shortFlags) flags[shortFlag] = true;\n continue;\n }\n }\n if (!token.startsWith(\"--\")) {\n args.push(token);\n continue;\n }\n\n const withoutPrefix = token.slice(2);\n const [rawKey, inlineValue] = withoutPrefix.split(\"=\", 2);\n if (inlineValue !== undefined) {\n flags[rawKey] = inlineValue;\n continue;\n }\n\n const nextToken = tokens[index + 1];\n if (nextToken && !nextToken.startsWith(\"--\")) {\n flags[rawKey] = nextToken;\n index += 1;\n continue;\n }\n\n flags[rawKey] = true;\n }\n\n const command = args[0] || \"help\";\n const expandedCommand = SHORT_COMMAND_ALIASES[command] || command;\n\n return {\n command: expandedCommand,\n subcommand: args[1] || \"\",\n args: args.slice(1),\n flags,\n follow: Boolean(flags.follow),\n };\n}\n\nconst COMMAND_HELP = {\n \"help\": {\n usage: \"llmproxy help [comando]\",\n description: \"Mostra la guida generale oppure la scheda dettagliata di un comando specifico.\",\n when: \"Usalo quando vuoi capire rapidamente quale comando scegliere o ricordarti sintassi e flusso consigliato.\",\n example: \"llmproxy help claude:setup\",\n },\n \"setup\": {\n usage: \"llmproxy setup\",\n description: \"Verifica il runtime locale, mostra il data root e il service manager selezionato.\",\n when: \"Usalo subito dopo l'installazione o quando vuoi capire dove llmproxy salva dati e log.\",\n example: \"llmproxy setup\",\n },\n \"version\": {\n usage: \"llmproxy version\",\n description: \"Stampa la versione corrente della CLI installata.\",\n when: \"Usalo per verificare che l'update sia andato a buon fine o per debug rapido della versione attiva.\",\n example: \"llmproxy --version\",\n },\n \"release-notes\": {\n usage: \"llmproxy release-notes [--version ] [--locale ]\",\n description: \"Stampa il changelog di una versione specifica usando commit message, note embedded o fallback locale.\",\n when: \"Usalo per controllare cosa introduce una release oppure per verificare l'output che verra' mostrato dopo un update.\",\n example: \"llmproxy release-notes --version 0.2.61\",\n },\n \"update\": {\n usage: \"llmproxy update\",\n description: \"Scarica e installa l'ultima versione dalla repository GitHub e verifica il binario aggiornato.\",\n when: \"Usalo quando vuoi aggiornare la CLI mantenendo una sola installazione globale attiva.\",\n example: \"llmproxy update\",\n },\n \"install\": {\n usage: \"llmproxy install\",\n description: \"Alias inglese di install:persistent-en: installa globalmente la CLI corrente e registra il servizio persistente nativo dell'OS.\",\n when: \"Usalo quando vuoi un comando piu' corto e leggibile per il bootstrap persistente in inglese dal binario CLI.\",\n example: \"llmproxy install\",\n englishDescription: \"English alias for install:persistent-en: installs the current CLI globally and registers the native persistent service for the OS.\",\n englishWhen: \"Use it when you want a shorter, English-first command for the persistent bootstrap flow from the CLI.\",\n englishExample: \"llmproxy install\",\n locale: \"en\",\n },\n \"install:persistent\": {\n usage: \"llmproxy install:persistent\",\n description: \"Installa globalmente la CLI corrente e registra il servizio persistente nativo dell'OS.\",\n when: \"Usalo come bootstrap one-shot dal checkout locale quando vuoi avere llmproxy disponibile anche dopo reboot.\",\n example: \"npm run install:persistent\",\n locale: \"it\",\n },\n \"install:persistent-it\": {\n usage: \"llmproxy install:persistent-it\",\n description: \"Installa globalmente la CLI corrente e registra il servizio persistente nativo dell'OS.\",\n when: \"Usalo come percorso esplicito in italiano quando vuoi output, help ed errori in italiano.\",\n example: \"npm run install:persistent-it\",\n locale: \"it\",\n },\n \"install:persistent-en\": {\n usage: \"llmproxy install:persistent-en\",\n description: \"Installs the current CLI globally and registers the native persistent service for the OS.\",\n when: \"Use it when you want the explicit English install path with English output, help, and errors.\",\n example: \"npm run install:persistent-en\",\n englishDescription: \"Installs the current CLI globally and registers the native persistent service for the OS.\",\n englishWhen: \"Use it when you want the explicit English install path with English output, help, and errors.\",\n englishExample: \"npm run install:persistent-en\",\n locale: \"en\",\n },\n \"uninstall\": {\n usage: \"llmproxy uninstall\",\n description: \"Disinstallazione completa: ferma il servizio, rimuove container Docker, dati, file di servizio e pacchetto globale.\",\n when: \"Usalo quando vuoi rimuovere completamente llmproxy dal sistema (servizio, Docker, dati, pacchetto).\",\n example: \"llmproxy uninstall\",\n },\n \"login\": {\n usage: \"llmproxy login\",\n description: \"Alias legacy di `llmproxy provider:add copilot`: autentica GitHub Copilot via device flow.\",\n when: \"Usalo solo per compatibilita'; il flusso consigliato e' `llmproxy provider:add copilot`.\",\n example: \"llmproxy provider:add copilot\",\n },\n \"logout\": {\n usage: \"llmproxy logout\",\n description: \"Rimuove i token Copilot locali.\",\n when: \"Usalo per forzare un nuovo login o per pulire completamente le credenziali locali.\",\n example: \"llmproxy logout\",\n },\n \"provider:add\": {\n usage: \"llmproxy provider:add --api-key --model --vision [--free-model [true|false]] [--name ] [--plan ]\",\n description: \"Aggiunge un provider noto: Copilot via device flow, gli altri provider tramite API key e modello predefinito. --vision e' obbligatorio per i provider API key. Usa --free-model per marcare la coppia provider/modello come gratuita e impedire all'auto-escalation di abbandonarla.\",\n when: \"Usalo quando vuoi configurare un provider di fallback aggiuntivo, per esempio openrouter o kimi, oltre al provider principale.\",\n example: \"llmproxy provider:add qwen --api-key sk-sp-... --model qwen3.7-plus --vision true --plan subscription\",\n },\n \"provider:key\": {\n usage: \"llmproxy provider:key --api-key [--model ] [--vision ] [--free-model ] [--plan ]\",\n description: \"Aggiorna o imposta la API key di un provider gia' configurato con autenticazione api_key. --vision e' opzionale (mantiene il valore esistente se omesso). --free-model puo' attivare o disattivare il blocco dell'auto-escalation per quella coppia provider/modello.\",\n when: \"Usalo quando devi ruotare una chiave esistente senza ricreare da zero il provider.\",\n example: \"llmproxy provider:key qwen --api-key sk-sp-... --vision true --plan subscription\",\n },\n \"provider:available\": {\n usage: \"llmproxy provider:available\",\n description: \"Elenca i provider supportati dalla CLI, con id canonico, alias e tipo di autenticazione.\",\n when: \"Usalo quando vuoi sapere quali provider puoi configurare prima di eseguire provider:add.\",\n example: \"llmproxy provider:available\",\n },\n \"provider:list\": {\n usage: \"llmproxy provider:list\",\n description: \"Elenca i provider configurati o, dentro un progetto con override Claude, la chain effettiva di fallback del progetto.\",\n when: \"Usalo per controllare rapidamente quali provider verranno davvero provati, con i modelli effettivi derivati da ANTHROPIC_DEFAULT_MODEL quando presente.\",\n example: \"llmproxy provider:list\",\n },\n \"provider:test\": {\n usage: \"llmproxy provider:test [--vision] [--all-proxies] [--proxy ] [--provider ]\",\n description: \"Testa i provider configurati con inferenza testuale. Usa --vision per testare la visione immagini; con --all-proxies o --proxy esegue inferenza su ogni combinazione provider × proxy.\",\n when: \"Usalo dopo aver configurato i provider per verificare che funzionino con un test di inferenza completo, oppure con --vision per testare la visione immagini.\",\n example: \"llmproxy provider:test\",\n englishExample: \"llmproxy provider:test\",\n },\n \"provider:status\": {\n usage: \"llmproxy provider:status\",\n description: \"Mostra il provider attivo e lo stato sintetico di tutti i provider configurati.\",\n when: \"Usalo quando vuoi capire quale provider sta servendo le richieste e se i fallback sono completi o mancanti di modello/chiave.\",\n example: \"llmproxy provider:status\",\n },\n \"provider:usage\": {\n usage: \"llmproxy provider:usage\",\n description: \"Mostra il consumo di token giornaliero, settimanale e mensile con breakdown per provider e modello.\",\n when: \"Usalo quando vuoi monitorare il consumo di token e capire quale provider/modello costa di piu'.\",\n example: \"llmproxy provider:usage\",\n },\n \"provider:order\": {\n usage: \"llmproxy provider:order \",\n description: \"Sposta un provider nella posizione di fallback desiderata.\",\n when: \"Usalo quando vuoi dare priorita' a un provider diverso senza doverlo rimuovere e ricreare.\",\n example: \"llmproxy provider:order kimi 1\",\n },\n \"provider:reorder\": {\n usage: \"llmproxy provider:reorder\",\n description: \"Forza subito un ciclo di reordering automatico (price/power/speed) senza aspettare il timer.\",\n when: \"Usalo dopo aver cambiato LLMPROXY_REORDERING, o quando vuoi un ordine aggiornato subito.\",\n example: \"llmproxy provider:reorder\",\n },\n \"provider:update\": {\n usage: \"llmproxy provider:update [--vision ] [--free-model ] [--name ]\",\n description: \"Aggiorna metadati di un provider gia' configurato senza toccare le credenziali o il modello. --vision abilita/disabilita la partecipazione al fallback delle richieste con immagini. --free-model attiva/disattiva il blocco dell'auto-escalation per quella coppia provider/modello. --name cambia il nome visualizzato.\",\n when: \"Usalo quando vuoi correggere velocemente il flag vision dopo un test, marcare un provider come gratuito gratuito, o rinominare un'istanza per distinguerla da altre dello stesso tipo.\",\n example: \"llmproxy provider:update opencode-bacin --vision false --free-model true --name 'OpenCode (Bacin)'\",\n },\n \"provider:rename\": {\n usage: \"llmproxy provider:rename \",\n description: \"Rinomina un provider configurato per distinguerlo meglio negli elenchi e nello stato.\",\n when: \"Usalo quando hai piu' provider simili e vuoi etichette piu' chiare per capire il fallback a colpo d'occhio.\",\n example: \"llmproxy provider:rename backup Backup Copilot\",\n },\n \"provider:remove\": {\n usage: \"llmproxy provider:remove \",\n description: \"Rimuove un provider configurato dal fallback locale.\",\n when: \"Usalo quando un provider non serve piu' oppure vuoi eliminare una configurazione non valida.\",\n example: \"llmproxy provider:remove openrouter\",\n },\n \"run\": {\n usage: \"llmproxy run\",\n description: \"Avvia l'istanza locale/dev in foreground.\",\n when: \"Usalo per debug rapido locale sulla porta 5045 senza toccare il servizio persistente.\",\n example: \"llmproxy run\",\n },\n \"stop\": {\n usage: \"llmproxy stop\",\n description: \"Ferma solo l'istanza locale/dev avviata con `llmproxy run` sulla porta 5045.\",\n when: \"Usalo quando vuoi chiudere la run locale senza fermare il servizio persistente.\",\n example: \"llmproxy stop\",\n },\n \"status\": {\n usage: \"llmproxy status [--docker]\",\n description: \"Mostra stato del servizio, autenticazione e ordine dei provider. Con --docker mostra anche lo stato dei container Docker.\",\n when: \"Usalo quando il proxy non risponde o vuoi verificare quale provider sia attivo. Aggiungi --docker per vedere i container in esecuzione.\",\n example: \"llmproxy status --docker\",\n },\n \"stats\": {\n usage: \"llmproxy stats\",\n description: \"Mostra statistiche aggregate di utilizzo con breakdown per provider e modello e relativo consumo token.\",\n when: \"Usalo quando vuoi capire quali provider e modelli stanno assorbendo piu' traffico e token.\",\n example: \"llmproxy stats\",\n },\n \"stats:reset\": {\n usage: \"llmproxy stats:reset [--hard]\",\n description: \"Azzera tutte le statistiche di utilizzo (metering records). Con --hard resetta anche la cache dello smart router e il file di configurazione auto-rank.\",\n when: \"Usalo quando vuoi ripartire da zero con le statistiche, per esempio dopo un cambio configurazione o per fare test puliti.\",\n example: \"llmproxy stats:reset --hard\",\n },\n \"logs\": {\n usage: \"llmproxy logs [--follow]\",\n description: \"Mostra i log recenti del servizio e dell'ultimo audit log JSONL.\",\n when: \"Usalo per diagnosticare errori; aggiungi --follow per seguire il flusso in tempo reale.\",\n example: \"llmproxy logs --follow\",\n },\n \"service:start\": {\n usage: \"llmproxy service:start\",\n description: \"Installa e avvia il servizio utente persistente.\",\n when: \"Usalo quando vuoi che il proxy resti disponibile dopo il reboot senza avviarlo a mano.\",\n example: \"llmproxy service:start\",\n },\n \"service:stop\": {\n usage: \"llmproxy service:stop\",\n description: \"Ferma il servizio persistente.\",\n when: \"Usalo prima di manutenzione, riconfigurazioni o disinstallazione.\",\n example: \"llmproxy service:stop\",\n },\n \"service:restart\": {\n usage: \"llmproxy service:restart\",\n description: \"Riavvia il servizio persistente e verifica che il runtime Docker/locale sia tornato sano.\",\n when: \"Usalo dopo update o modifiche alla configurazione runtime.\",\n example: \"llmproxy service:restart\",\n },\n \"service:runtime\": {\n usage: \"llmproxy service:runtime \",\n description: \"Passa in modo esplicito tra runtime Docker e runtime nativo, pulendo quello precedente e verificando l'health finale.\",\n when: \"Usalo quando vuoi migrare la macchina dalla variante Docker a quella launchd/systemd o viceversa con un solo comando.\",\n example: \"llmproxy service:runtime docker\",\n },\n \"models:list\": {\n usage: \"llmproxy models:list\",\n description: \"Elenca i modelli disponibili in forma numerata.\",\n when: \"Usalo prima di llmproxy claude:setup per scegliere l'indice corretto del modello.\",\n example: \"llmproxy models:list\",\n },\n \"model:set\": {\n usage: \"llmproxy model:set \",\n description: \"Aggiorna il modello Claude del progetto scrivendo il valore grezzo in .claude/settings.json.\",\n when: \"Usalo quando vuoi cambiare rapidamente routing o provider senza rifare claude:setup, ad esempio passando a deepseek:deepseek-v4-flash.\",\n example: \"llmproxy model:set deepseek:deepseek-v4-flash\",\n },\n \"config:list\": {\n usage: \"llmproxy config:list [--project|--service|--scope ]\",\n description: \"Mostra le variabili configurabili di progetto e/o servizio gestite da llmproxy.\",\n when: \"Usalo per verificare rapidamente gli override in .claude/settings.json e la configurazione persistita del servizio.\",\n example: \"llmproxy config:list --scope global\",\n },\n \"config:get\": {\n usage: \"llmproxy config:get [--project|--service|--scope ]\",\n description: \"Legge una variabile configurabile di llmproxy dal progetto o dal servizio.\",\n when: \"Usalo quando vuoi verificare il valore effettivo di una variabile prima di cambiarla.\",\n example: \"llmproxy config:get LLMPROXY_REORDERING --scope service\",\n },\n \"config:set\": {\n usage: \"llmproxy config:set [--project|--service|--scope ]\",\n description: \"Imposta una variabile configurabile di llmproxy nello scope corretto.\",\n when: \"Usalo per aggiornare routing di progetto o parametri runtime senza editare i file a mano.\",\n example: \"llmproxy config:set LLMPROXY_REORDERING price-speed-power --scope service\",\n },\n \"config:unset\": {\n usage: \"llmproxy config:unset [--project|--service|--scope ]\",\n description: \"Rimuove una variabile configurabile di llmproxy dal progetto o dal servizio.\",\n when: \"Usalo quando vuoi tornare al comportamento di default senza lasciare override residui.\",\n example: \"llmproxy config:unset ANTHROPIC_DEFAULT_MODEL --scope global\",\n },\n \"test\": {\n usage: \"llmproxy test [-i|--inference] [--all-providers] [--proxy ] [--provider ] [|]\",\n description: \"Esegue un probe rapido dei provider oppure, con -i, una vera inferenza contro il proxy locale. Con -i si puo' specificare un ID provider (es. opencode-alessio) o un indice numerico (es. 1) per testare un singolo provider invece della fallback chain. Con --proxy si forza un proxy specifico.\",\n when: \"Usalo per verificare rapidamente lo stato sintetico dei provider oppure quale provider risponde davvero a una richiesta /v1/messages.\",\n example: \"llmproxy test -i --all-providers\",\n },\n \"claude:setup\": {\n usage: \"llmproxy claude:setup [--model ]\",\n description: \"Scrive .claude/settings.json per usare llmproxy come backend locale di Claude Code.\",\n when: \"Usalo nel progetto che vuoi collegare a llmproxy, dopo aver configurato almeno un provider e dopo aver scelto il modello con models:list.\",\n example: \"llmproxy claude:setup --model 2\",\n },\n \"pi:setup\": {\n usage: \"llmproxy pi:setup\",\n description: \"Scrive .pi/models.json e .pi/settings.json per usare llmproxy come provider in PI Agent.\",\n when: \"Usalo nel progetto che vuoi usare con PI Agent, dopo aver configurato llmproxy con almeno un provider.\",\n example: \"llmproxy pi:setup\",\n },\n \"vscode-chat:setup\": {\n usage: \"llmproxy vscode-chat:setup\",\n description: \"Configura VS Code Chat con llmproxy come provider OpenAI, includendo tutti i modelli disponibili.\",\n when: \"Usalo per abilitare llmproxy come backend nella Chat di VS Code (Copilot Chat).\",\n example: \"llmproxy vscode-chat:setup\",\n },\n \"vscode-claude:setup\": {\n usage: \"llmproxy vscode-claude:setup [--model ]\",\n description: \"Configura l'estensione Claude Code in VS Code con llmproxy come backend e tutti i modelli disponibili.\",\n when: \"Usalo per collegare l'estensione Claude Code in VS Code al proxy locale llmproxy.\",\n example: \"llmproxy vscode-claude:setup --model 2\",\n },\n \"proxy:add\": {\n usage: \"llmproxy proxy:add [--name ]\",\n description: \"Aggiunge un proxy alla registry. Il formato URL include credenziali: http://user:pass@host:port. Con --name assegna un nome leggibile.\",\n when: \"Usalo per registrare un proxy da usare in rotazione con --proxy (senza valore).\",\n example: \"llmproxy proxy:add http://user:key@dc.decodo.com:10001 --name 'Proxy primario'\",\n },\n \"proxy:list\": {\n usage: \"llmproxy proxy:list\",\n description: \"Elenca tutti i proxy registrati in ordine di failover.\",\n when: \"Usalo per vedere i proxy disponibili e il loro ordine.\",\n example: \"llmproxy proxy:list\",\n },\n \"proxy:remove\": {\n usage: \"llmproxy proxy:remove \",\n description: \"Rimuove un proxy dalla registry per ID (dominio).\",\n when: \"Usalo per rimuovere un proxy non piu' valido.\",\n example: \"llmproxy proxy:remove dc.decodo.com\",\n },\n \"proxy:reorder\": {\n usage: \"llmproxy proxy:reorder ...\",\n description: \"Riordina i proxy specificando gli ID nel nuovo ordine di failover.\",\n when: \"Usalo per cambiare l'ordine di failover dei proxy.\",\n example: \"llmproxy proxy:reorder proxy-a.com proxy-b.com\",\n },\n \"proxy:test\": {\n usage: \"llmproxy proxy:test\",\n description: \"Testa tutti i proxy registrati verificando la connettivita'.\",\n when: \"Usalo per verificare quali proxy sono raggiungibili.\",\n example: \"llmproxy proxy:test\",\n },\n};\n\nfunction printCommandHelp(command, stdout = process.stdout) {\n const entry = COMMAND_HELP[command];\n if (!entry) {\n stdout.write(`Comando non documentato: ${command}\\n`);\n stdout.write(\"Usa `llmproxy help` per l'elenco completo.\\n\");\n return 1;\n }\n\n const shortAlias = REVERSE_SHORT_ALIASES[command] || \"\";\n const isEnglishInstallCommand = entry.locale === \"en\";\n const descriptionLabel = isEnglishInstallCommand ? \"Description\" : \"Descrizione\";\n const whenLabel = isEnglishInstallCommand ? \"When to use\" : \"Quando usarlo\";\n const exampleLabel = isEnglishInstallCommand ? \"Example\" : \"Esempio\";\n const description = isEnglishInstallCommand ? (entry.englishDescription || entry.description) : entry.description;\n const when = isEnglishInstallCommand ? (entry.englishWhen || entry.when) : entry.when;\n const example = isEnglishInstallCommand ? (entry.englishExample || entry.example) : entry.example;\n\n if (shortAlias) stdout.write(`Short: llmp ${shortAlias}\\n`);\n stdout.write(`${entry.usage}\\n\\n`);\n stdout.write(`${descriptionLabel}: ${description}\\n`);\n stdout.write(`${whenLabel}: ${when}\\n`);\n if (example) stdout.write(`${exampleLabel}: ${example}\\n`);\n return 0;\n}\n\nfunction printHelp(stdout = process.stdout) {\n const knownProvidersInline = formatKnownProvidersInline();\n const s = (cmd) => {\n const alias = REVERSE_SHORT_ALIASES[cmd];\n return alias ? `[llmp ${alias}]` : \"\";\n };\n stdout.write(`llmProxy CLI\n\nComandi principali:\n llmproxy help ${s(\"help\")} mostra questa guida\n llmproxy setup ${s(\"setup\")} verifica il runtime locale, il data root e il service manager selezionato\n llmproxy version ${s(\"version\")} mostra la versione corrente della CLI installata\n llmproxy install:persistent-it ${s(\"install:persistent-it\")} installa globalmente la CLI corrente e attiva il servizio persistente nativo dell'OS\n llmproxy install:persistent-en ${s(\"install:persistent-en\")} installs the current CLI globally and enables the native persistent service for the OS\n llmproxy install ${s(\"install\")} English alias for install:persistent-en\n llmproxy install:persistent ${s(\"install:persistent\")} alias legacy di install:persistent-it\n llmproxy update ${s(\"update\")} scarica e installa l'ultima versione dalla repo e verifica il binario aggiornato\n llmproxy uninstall ${s(\"uninstall\")} disinstallazione completa: servizio, Docker, dati e pacchetto globale\n\nEsecuzione proxy:\n llmproxy run avvia l'istanza locale/dev in foreground su 127.0.0.1:5045\n llmproxy stop ${s(\"stop\")} ferma solo l'istanza locale/dev su 127.0.0.1:5045\n llmproxy status ${s(\"status\")} mostra stato del servizio, autenticazione e ordine dei provider\n llmproxy stats ${s(\"stats\")} mostra statistiche aggregate di utilizzo per provider e modello\n llmproxy stats:reset [--hard] ${s(\"stats:reset\")} azzera le statistiche di utilizzo; con --hard resetta anche cache e auto-rank\n llmproxy logs [--follow] ${s(\"logs\")} legge i log recenti; usa --follow per seguire il flusso in tempo reale\n\nServizio persistente:\n llmproxy service:start ${s(\"service:start\")} installa e avvia il servizio utente persistente\n llmproxy service:stop ${s(\"service:stop\")} ferma il servizio persistente\n llmproxy service:restart ${s(\"service:restart\")} riavvia il servizio persistente dopo modifiche o update\n llmproxy service:runtime ${s(\"service:runtime\")} passa tra runtime docker e runtime nativo pulendo quello precedente\n\nProvider:\n llmproxy provider:add ... ${s(\"provider:add\")} aggiunge un provider noto (Copilot avvia il login; gli altri usano API key)\n llmproxy provider:key --api-key ${s(\"provider:key\")} aggiorna/imposta la API key per provider API-key\n llmproxy provider:available ${s(\"provider:available\")} elenca i provider supportati dalla CLI\n llmproxy provider:list ${s(\"provider:list\")} elenca i provider configurati o la chain effettiva del progetto corrente\n llmproxy provider:test [--vision] [--all-proxies] ${s(\"provider:test\")} inferenza testuale; con --vision testa la visione, con --all-proxies testa ogni proxy\n llmproxy provider:status ${s(\"provider:status\")} mostra quale provider e' attivo in questo momento\n llmproxy provider:usage ${s(\"provider:usage\")} mostra consumo token giornaliero, settimanale e mensile\n llmproxy provider:order ${s(\"provider:order\")} cambia la priorita' di fallback di un provider\n llmproxy provider:reorder ${s(\"provider:reorder\")} riordina i provider in base ai criteri LLMPROXY_REORDERING\n llmproxy provider:update ... ${s(\"provider:update\")} aggiorna vision, free-model o name di un provider\n llmproxy provider:rename ${s(\"provider:rename\")} rinomina un provider per distinguerlo meglio\n llmproxy provider:remove ${s(\"provider:remove\")} rimuove un provider configurato\n\nProxy (registro e rotazione):\n llmproxy proxy:add [--name] ${s(\"proxy:add\")} aggiunge un proxy alla registry\n llmproxy proxy:list ${s(\"proxy:list\")} elenca i proxy registrati in ordine di failover\n llmproxy proxy:remove ${s(\"proxy:remove\")} rimuove un proxy per ID (dominio)\n llmproxy proxy:reorder ... ${s(\"proxy:reorder\")} riordina i proxy (primo = prioritario)\n llmproxy proxy:test ${s(\"proxy:test\")} testa la connettivita' di tutti i proxy\n llmproxy provider:test --all-proxies ${s(\"provider:test\")} esegue un test reale di inferenza provider × proxy\n (usa --proxy senza valore per rotazione automatica al posto di --proxy )\n\nProvider noti:\n ${knownProvidersInline}\n\nModelli e Claude Code:\n llmproxy models:list ${s(\"models:list\")} mostra i modelli disponibili in forma numerata\n llmproxy model:set ${s(\"model:set\")} aggiorna il modello del progetto (es. deepseek:deepseek-v4-flash)\n llmproxy config:list [--scope ...] ${s(\"config:list\")} mostra le variabili configurabili gestite da llmproxy\n llmproxy config:get [...] ${s(\"config:get\")} legge una variabile configurabile\n llmproxy config:set [...] ${s(\"config:set\")} imposta una variabile configurabile\n llmproxy config:unset [...] ${s(\"config:unset\")} rimuove una variabile configurabile\n llmproxy test ${s(\"test\")} esegue un test rapido di inferenza contro il proxy locale\n llmproxy claude:setup [--model ] ${s(\"claude:setup\")} scrive .claude/settings.json per usare llmproxy come backend locale\n llmproxy pi:setup ${s(\"pi:setup\")} scrive .pi/models.json e settings.json per PI Agent\n llmproxy vscode-chat:setup ${s(\"vscode-chat:setup\")} configura VS Code Chat con llmproxy (tutti i modelli)\n llmproxy vscode-claude:setup [--model ] ${s(\"vscode-claude:setup\")} configura Claude Code in VS Code con llmproxy\n\nQuando usare cosa:\n 1. Prima installazione persistente: install:persistent-it oppure install:persistent-en -> provider:add -> models:list -> claude:setup\n 2. Uso come servizio: service:start -> status -> logs\n 3. Multi-account: provider:add -> provider:list -> provider:order\n 4. Proxy rotazione: proxy:add -> proxy:list -> provider:add ... --proxy ...\n 5. Aggiornamento CLI: update\n 6. Rimozione completa: uninstall\n\nProblemi comuni:\n - llmproxy non risponde: usa llmproxy status e poi llmproxy logs --follow\n - Claude Code non vede il proxy: rilancia llmproxy claude:setup nel progetto corretto\n - un modello fallisce: controlla llmproxy models:list e scegli un indice valido\n - il token Copilot non funziona: riesegui llmproxy provider:add copilot\n`);\n}\n\nfunction readPackageVersion(packageRoot) {\n const packageFile = path.join(packageRoot, \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(packageFile, \"utf8\"));\n return String(pkg.version || \"0.0.0\");\n}\n\nfunction compareReleaseVersions(left, right) {\n const normalize = (value) => String(value || \"\")\n .trim()\n .split(\".\")\n .map((part) => Number.parseInt(part, 10));\n const leftParts = normalize(left);\n const rightParts = normalize(right);\n if (leftParts.length === 0 || rightParts.length === 0) return null;\n if (leftParts.some((part) => !Number.isFinite(part)) || rightParts.some((part) => !Number.isFinite(part))) {\n return null;\n }\n const maxLength = Math.max(leftParts.length, rightParts.length);\n for (let index = 0; index < maxLength; index += 1) {\n const current = leftParts[index] ?? 0;\n const remote = rightParts[index] ?? 0;\n if (current > remote) return 1;\n if (current < remote) return -1;\n }\n return 0;\n}\n\nasync function readRemotePackageVersion(fetchFn, repo = \"alessiobacin/llmProxy\", options = {}) {\n if (typeof fetchFn !== \"function\") return \"\";\n const timeoutMs = Number.isFinite(Number(options.timeoutMs)) ? Math.max(1, Number(options.timeoutMs)) : 3000;\n const versionRequest = fetchFn(`https://raw.githubusercontent.com/${repo}/main/package.json`, {\n method: \"GET\",\n headers: { \"content-type\": \"application/json\" },\n });\n const response = await Promise.race([\n Promise.resolve(versionRequest),\n new Promise((resolve) => setTimeout(() => resolve(null), timeoutMs)),\n ]);\n if (!response || !response.ok) return \"\";\n const payload = await response.json();\n return String(payload?.version || \"\").trim();\n}\n\nfunction readEmbeddedReleaseNotes(packageRoot, version, locale = \"it\") {\n try {\n const notesFile = path.join(packageRoot, \"release-notes.json\");\n const notes = JSON.parse(fs.readFileSync(notesFile, \"utf8\"));\n const versionNotes = notes[String(version || \"\").trim()];\n if (!versionNotes) return [];\n const normalizedLocale = normalizeLocale(locale);\n return Array.isArray(versionNotes[normalizedLocale]) ? versionNotes[normalizedLocale].filter((note) => String(note || \"\").trim()) : [];\n } catch {\n return [];\n }\n}\n\nfunction shellQuote(value) {\n return `'${String(value).replace(/'/g, `'\"'\"'`)}'`;\n}\n\nfunction runCommand(commandRunner, command, args = []) {\n try {\n return commandRunner(command, args, { encoding: \"utf8\" });\n } catch (error) {\n return { status: 1, stdout: \"\", stderr: \"\", error };\n }\n}\n\nfunction commandIsAvailable(commandRunner, command, args = [\"--version\"]) {\n const result = runCommand(commandRunner, command, args);\n return result && result.status === 0;\n}\n\nfunction quoteWindowsCmdArg(value) {\n const raw = String(value ?? \"\");\n if (!raw) return \"\\\"\\\"\";\n if (/^[A-Za-z0-9_./:\\\\=-]+$/.test(raw)) return raw;\n return `\"${raw.replace(/\"/g, \"\\\"\\\"\")}\"`;\n}\n\nfunction getPlatformCommandCandidates(command, platform = process.platform) {\n const normalized = String(command || \"\").trim();\n if (!normalized) return [];\n if (platform !== \"win32\") return [normalized];\n if (normalized.includes(\".\") || normalized.includes(\"\\\\\") || normalized.includes(\"/\")) return [normalized];\n if (normalized === \"npm\" || normalized === \"pnpm\" || normalized === \"npx\") {\n return [`${normalized}.cmd`, normalized];\n }\n return [normalized];\n}\n\nfunction runPlatformCommand(commandRunner, command, args = [], platform = process.platform) {\n let lastResult = null;\n for (const candidate of getPlatformCommandCandidates(command, platform)) {\n const result = platform === \"win32\" && candidate.toLowerCase().endsWith(\".cmd\")\n ? runCommand(commandRunner, \"cmd.exe\", [\"/d\", \"/s\", \"/c\", [quoteWindowsCmdArg(candidate), ...args.map((arg) => quoteWindowsCmdArg(arg))].join(\" \")])\n : runCommand(commandRunner, candidate, args);\n lastResult = result;\n if (result && result.status === 0) return result;\n const errorCode = result?.error?.code || result?.code || \"\";\n const stderrText = String(result?.stderr || \"\");\n const missingCommand = errorCode === \"ENOENT\" || /not recognized|cannot find/i.test(stderrText);\n if (!missingCommand) return result;\n }\n return lastResult || { status: 1, stdout: \"\", stderr: \"\" };\n}\n\nfunction commandIsAvailableForPlatform(commandRunner, command, platform = process.platform, args = [\"--version\"]) {\n const result = runPlatformCommand(commandRunner, command, args, platform);\n return result && result.status === 0;\n}\n\nfunction canWritePath(targetPath) {\n if (!targetPath || !fs.existsSync(targetPath)) return false;\n try {\n fs.accessSync(targetPath, fs.constants.W_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction canUseSudoNonInteractively(commandRunner) {\n if (!commandIsAvailable(commandRunner, \"sudo\")) return false;\n const result = runCommand(commandRunner, \"sudo\", [\"-n\", \"true\"]);\n return !!result && result.status === 0;\n}\n\nfunction isWindowsSessionElevated(commandRunner = spawnSync) {\n const result = runCommand(commandRunner, \"powershell.exe\", [\n \"-NoProfile\",\n \"-Command\",\n \"$principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()); if ($principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { '1' } else { '0' }\",\n ]);\n return result && result.status === 0 && String(result.stdout || \"\").trim() === \"1\";\n}\n\nfunction getWindowsServiceState(commandRunner = spawnSync, label = \"llmproxy\") {\n const result = runCommand(commandRunner, \"sc.exe\", [\"query\", label]);\n const stdout = String(result?.stdout || \"\");\n const stderr = String(result?.stderr || \"\");\n const combined = `${stdout}\\n${stderr}`;\n const normalized = combined.toLowerCase();\n const missing = /does not exist|1060/i.test(normalized);\n const active = normalized.includes(\"running\") || /\\b(stato|state)\\b[\\s:]+4\\b/i.test(combined);\n return {\n ok: !!result && result.status === 0,\n missing,\n active,\n stdout,\n stderr,\n };\n}\n\nfunction readOsReleaseInfo(options = {}) {\n const platform = String(options.platform || process.platform);\n if (platform !== \"linux\") return {};\n const content = typeof options.content === \"string\"\n ? options.content\n : (fs.existsSync(\"/etc/os-release\") ? fs.readFileSync(\"/etc/os-release\", \"utf8\") : \"\");\n if (!content) return {};\n\n const values = {};\n for (const rawLine of content.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const separatorIndex = line.indexOf(\"=\");\n if (separatorIndex <= 0) continue;\n const key = line.slice(0, separatorIndex).trim();\n let value = line.slice(separatorIndex + 1).trim();\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1);\n }\n values[key] = value;\n }\n return values;\n}\n\nfunction detectLinuxPackageFamily(osRelease = {}) {\n const tokens = [\n osRelease.ID,\n osRelease.ID_LIKE,\n osRelease.NAME,\n osRelease.PRETTY_NAME,\n ]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n\n if (/(ubuntu|debian|mint|pop)/.test(tokens)) return \"debian\";\n if (/(rhel|fedora|centos|rocky|almalinux)/.test(tokens)) return \"rhel\";\n if (/(arch|manjaro)/.test(tokens)) return \"arch\";\n return \"generic\";\n}\n\nfunction getPrerequisiteFixCommands(key, options = {}) {\n const locale = normalizeLocale(options.locale);\n const platform = String(options.platform || process.platform);\n const linuxFamily = detectLinuxPackageFamily(options.osRelease);\n const localPrefixCommands = locale === \"en\"\n ? [\n \"mkdir -p \\\"$HOME/.local/npm\\\"\",\n \"npm config set prefix \\\"$HOME/.local/npm\\\"\",\n \"echo 'export PATH=\\\"$HOME/.local/npm/bin:$PATH\\\"' >> ~/.profile\",\n ]\n : [\n \"mkdir -p \\\"$HOME/.local/npm\\\"\",\n \"npm config set prefix \\\"$HOME/.local/npm\\\"\",\n \"echo 'export PATH=\\\"$HOME/.local/npm/bin:$PATH\\\"' >> ~/.profile\",\n ];\n\n if (platform === \"darwin\") {\n if (key === \"npm\") return [\"brew install node\"];\n if (key === \"docker\" || key === \"docker-compose\") return [\"brew install --cask docker\"];\n if (key === \"docker-daemon\") return [\"open -a Docker\"];\n if (key === \"npm-write\") return localPrefixCommands;\n return [];\n }\n\n if (platform === \"win32\") {\n if (key === \"npm\") return [\"Scarica e installa Node.js da https://nodejs.org (versione LTS 22.x)\"];\n if (key === \"npm-write\") return locale === \"en\"\n ? [\"Run PowerShell as Administrator: npm config set prefix \\\"%APPDATA%\\\\npm\\\"\"]\n : [\"Esegui PowerShell come Amministratore: npm config set prefix \\\"%APPDATA%\\\\npm\\\"\"];\n if (key === \"windows-service-admin\") return locale === \"en\"\n ? [\"Run PowerShell as Administrator, then retry: llmp up\", \"Or stop the service as Administrator first: llmp service:stop\"]\n : [\"Esegui PowerShell come Amministratore, poi riprova: llmp up\", \"Oppure arresta prima il servizio come Amministratore: llmp service:stop\"];\n return [];\n }\n\n if (platform !== \"linux\") return [];\n\n if (key === \"systemd-user-bus\") return locale === \"en\"\n ? [\"loginctl enable-linger\", \"sudo loginctl enable-linger $USER\"]\n : [\"loginctl enable-linger\", \"sudo loginctl enable-linger $USER\"];\n\n if (linuxFamily === \"debian\") {\n if (key === \"npm\") return [\"sudo apt update && sudo apt install -y nodejs npm\"];\n if (key === \"docker\" || key === \"docker-compose\") return [\"sudo apt update && sudo apt install -y docker.io docker-compose-v2\"];\n if (key === \"docker-daemon\") return [\"sudo systemctl enable --now docker\", \"sudo usermod -aG docker \\\"$USER\\\"\"];\n if (key === \"systemctl\") return [\"sudo apt update && sudo apt install -y systemd\"];\n if (key === \"npm-write\") return localPrefixCommands;\n return [];\n }\n\n if (linuxFamily === \"rhel\") {\n if (key === \"npm\") return [\"sudo dnf install -y nodejs npm\"];\n if (key === \"docker\" || key === \"docker-compose\") return [\"sudo dnf install -y docker docker-compose-plugin\"];\n if (key === \"docker-daemon\") return [\"sudo systemctl enable --now docker\", \"sudo usermod -aG docker \\\"$USER\\\"\"];\n if (key === \"systemctl\") return [\"sudo dnf install -y systemd\"];\n if (key === \"npm-write\") return localPrefixCommands;\n return [];\n }\n\n if (linuxFamily === \"arch\") {\n if (key === \"npm\") return [\"sudo pacman -Sy --noconfirm nodejs npm\"];\n if (key === \"docker\" || key === \"docker-compose\") return [\"sudo pacman -Sy --noconfirm docker docker-compose\"];\n if (key === \"docker-daemon\") return [\"sudo systemctl enable --now docker\", \"sudo usermod -aG docker \\\"$USER\\\"\"];\n if (key === \"systemctl\") return [\"sudo pacman -Sy --noconfirm systemd\"];\n if (key === \"npm-write\") return localPrefixCommands;\n return [];\n }\n\n if (key === \"npm\") return [locale === \"en\" ? \"Install Node.js + npm with your distro package manager.\" : \"Installa Node.js + npm con il package manager della tua distro.\"];\n if (key === \"docker\" || key === \"docker-compose\") {\n return [locale === \"en\"\n ? \"Install Docker Engine plus Docker Compose plugin (or the legacy docker-compose binary).\"\n : \"Installa Docker Engine piu' il plugin Docker Compose (oppure il binario legacy docker-compose).\"];\n }\n if (key === \"docker-daemon\") return [\"sudo systemctl enable --now docker\", \"sudo usermod -aG docker \\\"$USER\\\"\"];\n if (key === \"systemctl\") {\n return [locale === \"en\"\n ? \"Use a Linux environment with systemd user services enabled.\"\n : \"Usa un ambiente Linux con servizi utente systemd abilitati.\"];\n }\n if (key === \"npm-write\") return localPrefixCommands;\n return [];\n}\n\nfunction resolveUpdatePrerequisites(options = {}) {\n const commandRunner = options.commandRunner || spawnSync;\n const targetPlatform = String(options.targetPlatform || process.platform);\n const env = options.env || process.env;\n const paths = options.paths || createPaths({ dataRoot: options.dataRoot, packageRoot: options.packageRoot, env });\n const packageRoot = paths.packageRoot;\n const runtimeEntry = resolveServiceEntryFile({ env, packageRoot, targetPlatform });\n const requiresDockerRuntime = path.basename(runtimeEntry) === \"docker-launchd-entry.js\";\n const dockerComposeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(packageRoot, \"docker-compose.production.yml\")).trim();\n const missing = [];\n\n const addMissing = (key, messageIt, messageEn) => {\n if (missing.some((item) => item.key === key)) return;\n missing.push({ key, messageIt, messageEn });\n };\n\n if (!commandIsAvailableForPlatform(commandRunner, \"git\", targetPlatform)) {\n addMissing(\"git\", \"Git (`git`) non trovato nel PATH.\", \"Git (`git`) is not available in PATH.\");\n }\n // pnpm is optional: we fall back to npm pack if pnpm is not available\n // if (!commandIsAvailable(commandRunner, \"pnpm\")) {\n // addMissing(\"pnpm\", \"pnpm non trovato nel PATH.\", \"pnpm is not available in PATH.\");\n // }\n const npmAvailable = commandIsAvailableForPlatform(commandRunner, \"npm\", targetPlatform);\n if (!npmAvailable) {\n addMissing(\"npm\", \"npm non trovato nel PATH.\", \"npm is not available in PATH.\");\n }\n\n if (npmAvailable) {\n const npmPrefixResult = runPlatformCommand(commandRunner, \"npm\", [\"prefix\", \"-g\"], targetPlatform);\n const npmPrefix = String(npmPrefixResult?.stdout || \"\").trim();\n if (!npmPrefixResult || npmPrefixResult.status !== 0 || !npmPrefix) {\n addMissing(\n \"npm-prefix\",\n \"Impossibile determinare il prefisso globale di npm (`npm prefix -g`).\",\n \"Unable to determine the global npm prefix (`npm prefix -g`).\",\n );\n } else if (!canWritePath(npmPrefix)) {\n if (!commandIsAvailableForPlatform(commandRunner, \"sudo\", targetPlatform)) {\n addMissing(\n \"npm-write\",\n `Il prefisso globale npm non e' scrivibile (${npmPrefix}) e \\`sudo\\` non e' disponibile.`,\n `The global npm prefix is not writable (${npmPrefix}) and \\`sudo\\` is not available.`,\n );\n } else if (!canUseSudoNonInteractively(commandRunner)) {\n addMissing(\n \"sudo-ticket\",\n `Il prefisso globale npm non e' scrivibile (${npmPrefix}) e questa CLI non puo' richiedere la password sudo in modo interattivo. Esegui prima \\`sudo -v\\` oppure rilancia \\`sudo llmproxy update\\`.`,\n `The global npm prefix is not writable (${npmPrefix}) and this CLI cannot prompt for a sudo password interactively. Run \\`sudo -v\\` first or rerun \\`sudo llmproxy update\\`.`,\n );\n }\n }\n }\n\n if (targetPlatform === \"win32\") {\n const serviceState = getWindowsServiceState(commandRunner, \"llmproxy\");\n if (serviceState.active && !isWindowsSessionElevated(commandRunner)) {\n addMissing(\n \"windows-service-admin\",\n \"Il servizio Windows `llmproxy` e' attivo e questa sessione PowerShell non e' elevata. Esegui `llmp up` da una PowerShell avviata come Amministratore, oppure arresta prima il servizio con privilegi amministrativi.\",\n \"The Windows `llmproxy` service is active and this PowerShell session is not elevated. Run `llmp up` from an Administrator PowerShell, or stop the service first with administrative privileges.\",\n );\n }\n }\n\n if (requiresDockerRuntime) {\n if (!fs.existsSync(dockerComposeFile)) {\n addMissing(\n \"docker-compose-file\",\n `File docker compose non trovato: ${dockerComposeFile}`,\n `Docker compose file not found: ${dockerComposeFile}`,\n );\n }\n const dockerAvailable = commandIsAvailableForPlatform(commandRunner, \"docker\", targetPlatform);\n if (!dockerAvailable) {\n addMissing(\"docker\", \"Docker non trovato nel PATH.\", \"Docker is not available in PATH.\");\n } else {\n if (!resolveDockerComposeCommand(commandRunner).ok) {\n addMissing(\n \"docker-compose\",\n \"Docker Compose non e' disponibile (`docker compose` e `docker-compose` falliscono).\",\n \"Docker Compose is not available (`docker compose` and `docker-compose` both failed).\",\n );\n }\n if (!commandIsAvailableForPlatform(commandRunner, \"docker\", targetPlatform, [\"info\"])) {\n addMissing(\n \"docker-daemon\",\n \"Il daemon Docker non e' raggiungibile per l'utente corrente (`docker info` fallisce).\",\n \"The Docker daemon is not reachable for the current user (`docker info` failed).\",\n );\n }\n }\n }\n\n return {\n ok: missing.length === 0,\n requiresDockerRuntime,\n dockerComposeFile,\n missing,\n };\n}\n\nfunction formatUpdatePrerequisitesFailure(result, locale = \"it\") {\n const normalizedLocale = normalizeLocale(locale);\n const header = normalizedLocale === \"en\"\n ? \"Update prerequisites are not satisfied. Install/fix the following items and run `llmproxy update` again:\"\n : \"I prerequisiti per `llmproxy update` non sono soddisfatti. Installa/correggi questi elementi e rilancia `llmproxy update`:\";\n const lines = [header];\n for (const item of result.missing || []) {\n lines.push(`- ${normalizedLocale === \"en\" ? item.messageEn : item.messageIt}`);\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction resolvePersistentInstallPrerequisites(options = {}) {\n const commandRunner = options.commandRunner || spawnSync;\n const env = options.env || process.env;\n const targetPlatform = String(options.targetPlatform || process.platform);\n const packageRoot = path.resolve(String(options.packageRoot || path.join(__dirname, \"..\")));\n const runtimeEnv = {\n ...env,\n LLMPROXY_ENV: String(env.LLMPROXY_ENV || env.NODE_ENV || \"production\"),\n };\n const runtimeEntry = resolveServiceEntryFile({ env: runtimeEnv, packageRoot: path.join(\"/tmp\", \"node_modules\", \"llmproxy\"), targetPlatform });\n const requiresDockerRuntime = path.basename(runtimeEntry) === \"docker-launchd-entry.js\";\n const dockerComposeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(packageRoot, \"docker-compose.production.yml\")).trim();\n const missing = [];\n\n const addMissing = (key, messageIt, messageEn) => {\n if (missing.some((item) => item.key === key)) return;\n missing.push({ key, messageIt, messageEn });\n };\n\n if (!fs.existsSync(packageRoot)) {\n addMissing(\n \"package-root\",\n `Directory del progetto non trovata: ${packageRoot}`,\n `Project directory not found: ${packageRoot}`,\n );\n }\n\n const npmAvailable = commandIsAvailableForPlatform(commandRunner, \"npm\", targetPlatform);\n if (!npmAvailable) {\n addMissing(\"npm\", \"npm non trovato nel PATH.\", \"npm is not available in PATH.\");\n }\n\n if (targetPlatform === \"win32\") {\n if (!commandIsAvailableForPlatform(commandRunner, \"powershell.exe\", targetPlatform, [\"-NoProfile\", \"-Command\", \"Get-Command\", \"sc.exe\"])) {\n addMissing(\n \"sc\",\n \"Windows Service Control (`sc.exe`) non disponibile. Windows non e' supportato correttamente.\",\n \"Windows Service Control (`sc.exe`) is not available. Windows is not fully supported.\",\n );\n }\n }\n\n if (targetPlatform === \"linux\" && !commandIsAvailableForPlatform(commandRunner, \"systemctl\", targetPlatform, [\"--version\"])) {\n addMissing(\n \"systemctl\",\n \"systemd (`systemctl`) non e' disponibile: l'installazione persistente Linux richiede un servizio `systemd --user`.\",\n \"systemd (`systemctl`) is not available: persistent Linux installs require a `systemd --user` service.\",\n );\n }\n\n if (targetPlatform === \"linux\" && commandIsAvailableForPlatform(commandRunner, \"systemctl\", targetPlatform, [\"--version\"])) {\n // Under sudo, delegate to real user via sudo -u (D-Bus enforces uid match on the socket)\n const sudoUser = process.env.SUDO_USER;\n const checkCmd = sudoUser\n ? `sudo -u \"${sudoUser}\" env XDG_RUNTIME_DIR=/run/user/$(id -u \"${sudoUser}\") DBUS_SESSION_BUS_ADDRESS=\"unix:path=/run/user/$(id -u \"${sudoUser}\")/bus\" systemctl --user is-system-running --quiet`\n : \"systemctl --user is-system-running --quiet\";\n const userBusCheck = runCommand(commandRunner, \"bash\", [\"-c\", checkCmd]);\n if (userBusCheck && userBusCheck.status !== 0 && userBusCheck.stderr && userBusCheck.stderr.includes(\"bus\")) {\n addMissing(\n \"systemd-user-bus\",\n \"Il bus systemd --user non e' raggiungibile. Attiva `loginctl enable-linger` per mantenere attivo il manager utente.\",\n \"The systemd --user bus is not reachable. Enable linger with `loginctl enable-linger` to keep the user manager running.\",\n );\n }\n }\n\n if (npmAvailable) {\n const npmPrefixResult = runPlatformCommand(commandRunner, \"npm\", [\"prefix\", \"-g\"], targetPlatform);\n const npmPrefix = String(npmPrefixResult?.stdout || \"\").trim();\n if (!npmPrefixResult || npmPrefixResult.status !== 0 || !npmPrefix) {\n addMissing(\n \"npm-prefix\",\n \"Impossibile determinare il prefisso globale di npm (`npm prefix -g`).\",\n \"Unable to determine the global npm prefix (`npm prefix -g`).\",\n );\n } else if (!canWritePath(npmPrefix)) {\n if (!commandIsAvailableForPlatform(commandRunner, \"sudo\", targetPlatform)) {\n addMissing(\n \"npm-write\",\n `Il prefisso globale npm non e' scrivibile (${npmPrefix}) e \\`sudo\\` non e' disponibile.`,\n `The global npm prefix is not writable (${npmPrefix}) and \\`sudo\\` is not available.`,\n );\n } else if (!canUseSudoNonInteractively(commandRunner)) {\n addMissing(\n \"sudo-ticket\",\n `Il prefisso globale npm non e' scrivibile (${npmPrefix}) e questa CLI non puo' richiedere la password sudo in modo interattivo. Esegui prima \\`sudo -v\\` oppure rilancia il comando con \\`sudo\\`.`,\n `The global npm prefix is not writable (${npmPrefix}) and this CLI cannot prompt for a sudo password interactively. Run \\`sudo -v\\` first or rerun the command with \\`sudo\\`.`,\n );\n }\n }\n }\n\n if (requiresDockerRuntime) {\n if (!fs.existsSync(dockerComposeFile)) {\n addMissing(\n \"docker-compose-file\",\n `File docker compose non trovato: ${dockerComposeFile}`,\n `Docker compose file not found: ${dockerComposeFile}`,\n );\n }\n const dockerCompose = resolveDockerComposeCommand(commandRunner);\n if (!dockerCompose.dockerAvailable) {\n addMissing(\"docker\", \"Docker non trovato nel PATH.\", \"Docker is not available in PATH.\");\n } else if (!dockerCompose.ok) {\n addMissing(\n \"docker-compose\",\n \"Docker Compose non e' disponibile (`docker compose` e `docker-compose` falliscono).\",\n \"Docker Compose is not available (`docker compose` and `docker-compose` both failed).\",\n );\n }\n if (dockerCompose.dockerAvailable && !commandIsAvailableForPlatform(commandRunner, \"docker\", targetPlatform, [\"info\"])) {\n addMissing(\n \"docker-daemon\",\n \"Il daemon Docker non e' raggiungibile per l'utente corrente (`docker info` fallisce).\",\n \"The Docker daemon is not reachable for the current user (`docker info` failed).\",\n );\n }\n }\n\n return {\n ok: missing.length === 0,\n missing,\n requiresDockerRuntime,\n dockerComposeFile,\n };\n}\n\nfunction formatPersistentInstallPrerequisitesFailure(result, options = {}) {\n const locale = normalizeLocale(options.locale);\n const platform = String(options.platform || process.platform);\n const osRelease = options.osRelease || {};\n const header = locale === \"en\"\n ? \"Persistent installation prerequisites are not satisfied. Install/fix the following items and run the command again:\"\n : \"I prerequisiti per l'installazione persistente non sono soddisfatti. Installa/correggi questi elementi e rilancia il comando:\";\n const lines = [header];\n\n for (const item of result.missing || []) {\n lines.push(`- ${locale === \"en\" ? item.messageEn : item.messageIt}`);\n const commands = getPrerequisiteFixCommands(item.key, { locale, platform, osRelease });\n for (const command of commands) {\n lines.push(` ${locale === \"en\" ? \"Command\" : \"Comando\"}: ${command}`);\n }\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction formatQwenPlanLabel(endpointVariant) {\n const variant = normalizeQwenEndpointVariant(endpointVariant);\n if (variant === \"token_plan\") return \"subscription\";\n if (variant === \"dashscope\") return \"payg\";\n return \"\";\n}\n\nfunction formatProviderPlanSuffix(provider, useColor = false) {\n if (String(provider?.provider || \"\").toLowerCase() !== \"qwen\") return \"\";\n const plan = formatQwenPlanLabel(provider?.endpoint_variant);\n return plan ? ` plan=${colorize(plan, \"cyan\", useColor)}` : \"\";\n}\n\nfunction formatVisionSuffix(provider, useColor = false) {\n if (provider.vision === true) return ` vision=${colorize(\"true\", \"green\", useColor)}`;\n if (provider.vision === false) return ` vision=${colorize(\"false\", \"red\", useColor)}`;\n return \"\";\n}\n\nfunction formatFreeModelSuffix(provider, useColor = false) {\n if (provider.free_model === true) return ` free=${colorize(\"true\", \"green\", useColor)}`;\n if (provider.free_model === false) return ` free=${colorize(\"false\", \"dim\", useColor)}`;\n return \"\";\n}\n\nfunction parseOptionalBooleanFlag(value, { defaultValue = null, allowBareTrue = false } = {}) {\n if (value === undefined || value === null || value === \"\") return defaultValue;\n if (value === true && allowBareTrue) return true;\n if (value === false) return false;\n const normalized = String(value).trim().toLowerCase();\n if (!normalized) return defaultValue;\n if (allowBareTrue && normalized === \"true\") return true;\n if (normalized === \"true\" || normalized === \"1\" || normalized === \"yes\" || normalized === \"on\") return true;\n if (normalized === \"false\" || normalized === \"0\" || normalized === \"no\" || normalized === \"off\") return false;\n return null;\n}\n\nfunction getProviderKind(provider) {\n return String(provider?.provider || \"copilot\").toLowerCase();\n}\n\nfunction resolveEffectiveProviderList(providers, configuredModel) {\n const baseProviders = Array.isArray(providers) ? providers.map((provider) => ({ ...provider })) : [];\n const preferences = parseProviderModelPreferences(configuredModel);\n if (!configuredModel || preferences.length === 0) {\n return {\n providers: baseProviders,\n configuredModel: \"\",\n projectOverrideActive: false,\n };\n }\n\n return {\n providers: baseProviders,\n configuredModel: String(configuredModel || \"\").trim(),\n projectOverrideActive: true,\n };\n}\n\nfunction formatProviderList(providers, options = {}) {\n const useColor = shouldUseColorOutput(options.stdout, options.env);\n const env = options.env || process.env;\n return providers.map((provider, index) => {\n const defaultModel = provider.effective_model || provider.default_model || (provider.provider === \"copilot\" ? DEFAULT_COPILOT_MODEL : \"\");\n const linePrefix = colorize(`${index + 1}.`, \"dim\", useColor);\n const providerId = colorize(provider.id, \"cyan\", useColor);\n const providerName = colorize(provider.name, \"bright\", useColor);\n const modelValue = defaultModel\n ? colorize(defaultModel, \"yellow\", useColor)\n : colorize(\"missing\", \"red\", useColor);\n const codingValue = colorize(provider.coding_info?.label || \"n/a\", provider.coding_info?.color || \"dim\", useColor);\n const creditValue = colorize(provider.credit_info?.label || \"n/a\", provider.credit_info?.color || \"dim\", useColor);\n const currentPrice = colorize(provider.price_info?.currentPriceLabel || \"n/a\", provider.price_info?.currentPriceColor || \"dim\", useColor);\n const bestProvider = colorize(provider.price_info?.bestProviderLabel || \"n/a\", provider.price_info?.bestProviderColor || \"dim\", useColor);\n const bestPrice = colorize(provider.price_info?.bestPriceLabel || \"n/a\", provider.price_info?.bestPriceColor || \"dim\", useColor);\n\n let proxyValue = null;\n if (provider.proxy_rotation) {\n proxyValue = colorize(\"proxy:rotation\", \"magenta\", useColor);\n } else if (provider.proxy_url) {\n proxyValue = colorize(`proxy:${provider.proxy_url}`, \"magenta\", useColor);\n } else {\n proxyValue = colorize(\"proxy:no\", \"dim\", useColor);\n }\n const benchSpeed = provider.bench_metric || \"n/a\";\n const visionSuffix = formatVisionSuffix(provider, useColor);\n const freeModelSuffix = formatFreeModelSuffix(provider, useColor);\n const planSuffix = formatProviderPlanSuffix(provider, useColor);\n const visibilityLine = [visionSuffix.trim(), freeModelSuffix.trim()].filter(Boolean).join(\" \");\n const details = [\n ` model=${modelValue}`,\n ` credit=${creditValue}`,\n ` coding=${codingValue}${planSuffix}`,\n visibilityLine ? ` ${visibilityLine}` : null,\n ` price=${currentPrice}`,\n ` best=${bestProvider} (${bestPrice})`,\n proxyValue ? ` proxy=${proxyValue}` : null,\n ` bench=${benchSpeed}`,\n ].filter(Boolean);\n\n return `${linePrefix} ${providerId} (${providerName})\\n${details.join(\"\\n\")}`;\n }).join(\"\\n\\n\");\n}\n\nfunction shouldUseColorOutput(stream = process.stdout, env = process.env) {\n if (String(env?.NO_COLOR || \"\").trim()) return false;\n if (String(env?.FORCE_COLOR || \"\").trim()) return true;\n return Boolean(stream && stream.isTTY);\n}\n\nfunction colorize(text, style, enabled) {\n if (!enabled) return String(text);\n const STYLES = {\n dim: \"\\x1b[2m\",\n bright: \"\\x1b[1m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n blue: \"\\x1b[34m\",\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n };\n const prefix = STYLES[style];\n if (!prefix) return String(text);\n return `${prefix}${text}\\x1b[0m`;\n}\n\nfunction formatProviderCreditSuffix(creditInfo, useColor = false) {\n const label = String(creditInfo?.label || \"n/a\").trim() || \"n/a\";\n const color = label === \"n/a\"\n ? \"dim\"\n : label === \"unavailable\"\n ? \"red\"\n : label.includes(\"-\")\n ? \"red\"\n : \"blue\";\n return ` credit=${colorize(label, color, useColor)}`;\n}\n\nfunction formatProviderPriceSuffix(priceInfo, useColor = false) {\n const currentPrice = String(priceInfo?.currentPriceLabel || \"n/a\").trim() || \"n/a\";\n const bestProvider = String(priceInfo?.bestProviderLabel || \"n/a\").trim() || \"n/a\";\n const bestPrice = String(priceInfo?.bestPriceLabel || \"n/a\").trim() || \"n/a\";\n const currentColor = currentPrice === \"n/a\" || currentPrice === \"unavailable\" ? \"dim\" : \"magenta\";\n const bestProviderColor = bestProvider === \"n/a\" || bestProvider === \"unavailable\" ? \"dim\" : \"green\";\n const bestPriceColor = bestPrice === \"n/a\" || bestPrice === \"unavailable\" ? \"dim\" : \"green\";\n return ` price=${colorize(currentPrice, currentColor, useColor)} best=${colorize(bestProvider, bestProviderColor, useColor)} (${colorize(bestPrice, bestPriceColor, useColor)})`;\n}\n\nfunction formatCodingScoreLabel(value) {\n if (value == null || value === \"\") return \"n/a\";\n const numeric = Number(value);\n if (!Number.isFinite(numeric)) return \"n/a\";\n return numeric.toFixed(1);\n}\n\nfunction colorForCodingScore(label) {\n if (label === \"n/a\") return \"dim\";\n if (label === \"unavailable\") return \"red\";\n const numeric = Number(label);\n if (!Number.isFinite(numeric)) return \"dim\";\n if (numeric >= 0.8 || numeric >= 80) return \"green\";\n if (numeric >= 0.6 || numeric >= 60) return \"yellow\";\n return \"red\";\n}\n\nfunction normalizeModelLookupCandidates(model) {\n const raw = String(model || \"\").trim();\n if (!raw) return [];\n const withoutDate = raw.replace(/-\\d{8}$/i, \"\");\n const leaf = raw.includes(\"/\") ? raw.split(\"/\").pop() : raw;\n const leafWithoutDate = leaf.replace(/-\\d{8}$/i, \"\");\n return Array.from(new Set([raw, withoutDate, leaf, leafWithoutDate]\n .map((entry) => String(entry || \"\").trim().toLowerCase())\n .filter(Boolean)));\n}\n\nasync function fetchProviderCodingInfo(provider, fetchFn, cache = new Map()) {\n // Prefer stored coding_score (backfilled at provider insertion, no need to fetch live every time)\n if (typeof provider?.coding_score === \"number\" && Number.isFinite(provider.coding_score)) {\n const label = formatCodingScoreLabel(provider.coding_score);\n return { label, color: colorForCodingScore(label) };\n }\n const model = String(provider?.effective_model || provider?.default_model || \"\").trim();\n if (!model) return { label: \"n/a\", color: \"dim\" };\n const score = await fetchCodingScore(model, fetchFn, cache);\n if (score == null) return { label: \"n/a\", color: \"dim\" };\n const label = formatCodingScoreLabel(score);\n return { label, color: colorForCodingScore(label) };\n}\n\nfunction formatCreditAmount(amount, currency = \"\") {\n const numeric = Number(amount);\n if (!Number.isFinite(numeric)) return \"\";\n const formatted = numeric.toFixed(2);\n const normalizedCurrency = String(currency || \"\").trim().toUpperCase();\n return normalizedCurrency ? `${normalizedCurrency} ${formatted}` : formatted;\n}\n\nasync function fetchProviderCreditInfo(provider, fetchFn, cache = new Map()) {\n const providerKind = String(provider?.provider || provider?.id || \"\").trim().toLowerCase();\n const accessToken = String(provider?.access_token || \"\").trim();\n const endpointVariant = String(provider?.endpoint_variant || \"\").trim().toLowerCase();\n if (!providerKind || !accessToken) return { label: \"n/a\", color: \"dim\" };\n\n const cacheKey = crypto\n .createHash(\"sha1\")\n .update(`${providerKind}|${endpointVariant}|${accessToken}`)\n .digest(\"hex\");\n\n if (cache.has(cacheKey)) return cache.get(cacheKey);\n\n const promise = (async () => {\n const unsupported = { label: \"n/a\", color: \"dim\" };\n const unavailable = { label: \"unavailable\", color: \"red\" };\n const request = {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"content-type\": \"application/json\",\n },\n signal: typeof AbortSignal?.timeout === \"function\" ? AbortSignal.timeout(5000) : undefined,\n };\n\n try {\n if (providerKind === \"deepseek\") {\n const response = await fetchFn(\"https://api.deepseek.com/user/balance\", request);\n if (!response?.ok) return unavailable;\n const payload = await readJsonResponseSafe(response);\n const balances = Array.isArray(payload?.balance_infos) ? payload.balance_infos : [];\n const preferred = balances.find((entry) => String(entry?.currency || \"\").trim().toUpperCase() === \"USD\") || balances[0];\n const label = formatCreditAmount(preferred?.total_balance, preferred?.currency);\n return label ? { label, color: \"blue\" } : unavailable;\n }\n\n if (providerKind === \"kimi\") {\n const response = await fetchFn(\"https://api.moonshot.ai/v1/users/me/balance\", request);\n if (!response?.ok) return unavailable;\n const payload = await readJsonResponseSafe(response);\n const label = formatCreditAmount(payload?.data?.available_balance);\n return label ? { label, color: \"blue\" } : unavailable;\n }\n\n if (providerKind === \"openrouter\") {\n const response = await fetchFn(\"https://openrouter.ai/api/v1/credits\", request);\n if (!response?.ok) return unavailable;\n const payload = await readJsonResponseSafe(response);\n const totalCredits = Number(payload?.data?.total_credits);\n const totalUsage = Number(payload?.data?.total_usage);\n if (!Number.isFinite(totalCredits) || !Number.isFinite(totalUsage)) return unavailable;\n const remainingCredits = Math.max(0, totalCredits - totalUsage);\n return { label: `${remainingCredits.toFixed(2)} credits`, color: \"blue\" };\n }\n\n return unsupported;\n } catch {\n return unavailable;\n }\n })();\n\n cache.set(cacheKey, promise);\n return promise;\n}\n\nfunction mapCloudPriceProviderLabel(providerId) {\n const normalized = String(providerId || \"\").trim().toLowerCase();\n const mappings = {\n alibaba_qwen: \"qwen\",\n fireworks_ai: \"fireworks\",\n moonshot_ai: \"kimi\",\n moonshotai: \"kimi\",\n vercel_ai_gateway: \"vercel_ai_gateway\",\n azure_aifoundry: \"azure_aifoundry\",\n };\n return mappings[normalized] || normalized;\n}\n\nfunction formatCloudPriceBreakdownLabel(entry) {\n const breakdown = Array.isArray(entry?.breakdown) ? entry.breakdown : [];\n const input = breakdown.find((item) => String(item?.dimension || \"\").trim().toLowerCase() === \"input\");\n const output = breakdown.find((item) => String(item?.dimension || \"\").trim().toLowerCase() === \"output\");\n const inputPrice = Number(input?.unit_price);\n const outputPrice = Number(output?.unit_price);\n if (!Number.isFinite(inputPrice) && !Number.isFinite(outputPrice)) return \"n/a\";\n const parts = [];\n if (Number.isFinite(inputPrice)) parts.push(`in=USD ${inputPrice.toFixed(2)}/1M`);\n if (Number.isFinite(outputPrice)) parts.push(`out=USD ${outputPrice.toFixed(2)}/1M`);\n return parts.join(\" \");\n}\n\nfunction pickCloudPriceBestProvider(resultProviders, provider) {\n const candidates = Array.isArray(resultProviders) ? resultProviders : [];\n if (candidates.length === 0) return null;\n const currentIds = new Set(mapProviderToCloudPriceIds(provider));\n const llmProxySupported = new Set(AVAILABLE_PROVIDER_SPECS.map((entry) => entry.id));\n const preferredAlternative = candidates.find((candidate) => {\n const mapped = mapCloudPriceProviderLabel(candidate?.provider_id);\n return mapped && !currentIds.has(String(candidate?.provider_id || \"\").trim().toLowerCase()) && llmProxySupported.has(mapped);\n });\n if (preferredAlternative) return preferredAlternative;\n const nonCurrent = candidates.find((candidate) => !currentIds.has(String(candidate?.provider_id || \"\").trim().toLowerCase()));\n if (nonCurrent) return nonCurrent;\n return candidates[0];\n}\n\nasync function fetchProviderPriceInfo(provider, fetchFn, cache = new Map()) {\n if (provider?.free_model === true) {\n return {\n currentPriceLabel: \"free\",\n currentPriceColor: \"green\",\n bestProviderLabel: \"free\",\n bestProviderColor: \"green\",\n bestPriceLabel: \"free\",\n bestPriceColor: \"green\",\n };\n }\n const model = String(provider?.effective_model || provider?.default_model || \"\").trim();\n if (!model) {\n return {\n currentPriceLabel: \"n/a\",\n currentPriceColor: \"dim\",\n bestProviderLabel: \"n/a\",\n bestProviderColor: \"dim\",\n bestPriceLabel: \"n/a\",\n bestPriceColor: \"dim\",\n };\n }\n const data = await fetchModelPricing(model, fetchFn, cache);\n if (!data) {\n return {\n currentPriceLabel: \"unavailable\",\n currentPriceColor: \"dim\",\n bestProviderLabel: \"unavailable\",\n bestProviderColor: \"dim\",\n bestPriceLabel: \"unavailable\",\n bestPriceColor: \"dim\",\n };\n }\n\n const currentOption = pickCurrentPricingOption(data.options, provider);\n const bestProvider = pickCloudPriceBestProvider(data?.result?.providers, provider);\n const currentPriceLabel = currentOption ? formatCloudPriceBreakdownLabel(currentOption) : \"n/a\";\n const bestProviderLabel = bestProvider ? mapCloudPriceProviderLabel(bestProvider.provider_id) : \"n/a\";\n const bestPriceLabel = data?.result ? formatCloudPriceBreakdownLabel(data.result) : \"n/a\";\n return {\n currentPriceLabel,\n currentPriceColor: currentPriceLabel === \"n/a\" || currentPriceLabel === \"unavailable\" ? \"dim\" : \"magenta\",\n bestProviderLabel,\n bestProviderColor: bestProviderLabel === \"n/a\" || bestProviderLabel === \"unavailable\" ? \"dim\" : \"green\",\n bestPriceLabel,\n bestPriceColor: bestPriceLabel === \"n/a\" || bestPriceLabel === \"unavailable\" ? \"dim\" : \"green\",\n };\n}\n\nfunction formatProviderStatus(providers) {\n const activeProviderId = providers[0]?.id || \"none\";\n const lines = [`Active provider: ${activeProviderId}`];\n providers.forEach((provider, index) => {\n const activeSuffix = index === 0 ? \" [active]\" : \"\";\n const defaultModel = provider.effective_model || provider.default_model || (provider.provider === \"copilot\" ? DEFAULT_COPILOT_MODEL : \"\");\n const state = provider.access_token && (provider.provider === \"copilot\" || defaultModel) ? \"configured\" : \"incomplete\";\n const modelSuffix = defaultModel ? ` model=${defaultModel}` : \" model=missing\";\n const visionSuffix = formatVisionSuffix(provider);\n const planSuffix = formatProviderPlanSuffix(provider);\n const visionLine = visionSuffix ? ` ${visionSuffix.trim()}` : \"\";\n lines.push(`${index + 1}. ${provider.id} (${provider.name})${activeSuffix} provider=${provider.provider || \"copilot\"} auth=${provider.auth_type || \"oauth\"} state=${state}${modelSuffix}${planSuffix}${visionLine ? \"\\n\" + visionLine : \"\"}`);\n });\n return lines.join(\"\\n\");\n}\n\nfunction formatConfigEntries(entries) {\n return entries\n .map((entry) => `${entry.scope}.${entry.key}=${entry.value == null ? \"\" : String(entry.value)}`)\n .join(\"\\n\");\n}\n\nfunction resolveQwenEndpointVariant(providerId, planFlag, apiKey) {\n if (providerId !== \"qwen\") return \"\";\n const explicit = normalizeQwenEndpointVariant(planFlag);\n if (explicit) return explicit;\n return /^sk-sp-/i.test(String(apiKey || \"\").trim()) ? \"token_plan\" : \"dashscope\";\n}\n\nfunction formatModelList(models) {\n return models.map((model, index) => `${index + 1}. ${model}`).join(\"\\n\");\n}\n\nfunction sortBreakdownEntries(entriesObject = {}) {\n return Object.entries(entriesObject)\n .map(([key, value]) => [key, value || {}])\n .sort((left, right) => {\n const leftTotal = Number(left[1].tokens_input || 0) + Number(left[1].tokens_output || 0);\n const rightTotal = Number(right[1].tokens_input || 0) + Number(right[1].tokens_output || 0);\n if (rightTotal !== leftTotal) return rightTotal - leftTotal;\n const leftRequests = Number(left[1].requests || 0);\n const rightRequests = Number(right[1].requests || 0);\n if (rightRequests !== leftRequests) return rightRequests - leftRequests;\n return String(left[0]).localeCompare(String(right[0]));\n });\n}\n\nfunction formatBreakdownSection(title, entriesObject = {}, useColor = false) {\n const entries = sortBreakdownEntries(entriesObject);\n if (entries.length === 0) {\n return {\n header: title,\n rows: [],\n columns: [\"Name\", \"Req\", \"Total\", \"In\", \"Out\"],\n };\n }\n const rows = entries.map(([key, value]) => {\n const input = Number(value.tokens_input || 0);\n const output = Number(value.tokens_output || 0);\n const total = input + output;\n const requests = Number(value.requests || 0);\n return { name: key, requests, total, input, output };\n });\n return { header: title, rows, columns: [\"Name\", \"Req\", \"Total\", \"In\", \"Out\"] };\n}\n\nfunction padRight(text, width) {\n const str = String(text);\n return str.length >= width ? str : str + \" \".repeat(width - str.length);\n}\n\nfunction padLeft(text, width) {\n const str = String(text);\n return str.length >= width ? str : \" \".repeat(width - str.length) + str;\n}\n\nfunction formatNumber(n) {\n return Number(n).toLocaleString(\"it-IT\");\n}\n\nfunction renderStatsTable(stats = {}, source = \"unknown\", useColor = false) {\n const totalRequests = Number(stats.total_requests || 0);\n const successCount = Number(stats.success_count || 0);\n const errorCount = Number(stats.error_count || 0);\n const input = Number(stats.total_tokens_input || 0);\n const output = Number(stats.total_tokens_output || 0);\n const total = Number(stats.total_tokens || 0);\n const providers = formatBreakdownSection(\"Providers\", stats.by_provider || {}, useColor);\n const models = formatBreakdownSection(\"Models\", stats.by_model || {}, useColor);\n\n const sections = [\n { label: \"Source\", value: source },\n { label: \"Requests\", value: `${formatNumber(totalRequests)} | Success: ${formatNumber(successCount)} | Errors: ${formatNumber(errorCount)}` },\n { label: \"Tokens\", value: `total=${formatNumber(total)} in=${formatNumber(input)} out=${formatNumber(output)}` },\n ];\n if (stats.earliest_timestamp || stats.latest_timestamp) {\n sections.push({ label: \"Range\", value: `${stats.earliest_timestamp || \"n/a\"} → ${stats.latest_timestamp || \"n/a\"}` });\n }\n\n const allRows = [\n ...providers.rows.map((r) => ({ ...r, section: \"Providers\" })),\n ...models.rows.map((r) => ({ ...r, section: \"Models\" })),\n ];\n\n const nameWidth = Math.max(\n 5,\n ...allRows.map((r) => r.name.length),\n ...[\"Name\", \"Provider\", \"Model\"].map((s) => s.length),\n );\n const numCols = [\"requests\", \"total\", \"input\", \"output\"];\n const colWidths = {};\n for (const col of numCols) {\n colWidths[col] = Math.max(\n 5,\n ...allRows.map((r) => formatNumber(r[col]).length),\n );\n }\n\n const border = \"─\";\n const pipe = \"│\";\n const cross = \"┼\";\n const topLeft = \"┌\";\n const topRight = \"┐\";\n const botLeft = \"└\";\n const botRight = \"┘\";\n const midLeft = \"├\";\n const midRight = \"┤\";\n\n const innerWidth = nameWidth + colWidths.requests + colWidths.total + colWidths.input + colWidths.output + 20;\n const width = Math.max(innerWidth, 50);\n\n function horizontalBar(left, mid, right, fill = border) {\n return left + fill.repeat(width - 2) + right;\n }\n\n function sectionDivider(label) {\n const labelText = `─ ${label} `;\n const remaining = width - 2 - labelText.length;\n return midLeft + labelText + border.repeat(Math.max(0, remaining)) + midRight;\n }\n\n function tableHeader() {\n const nameCol = padRight(\"Name\", nameWidth);\n const reqCol = padLeft(\"Req\", colWidths.requests);\n const totalCol = padLeft(\"Total\", colWidths.total);\n const inCol = padLeft(\"In\", colWidths.input);\n const outCol = padLeft(\"Out\", colWidths.output);\n const row = `${pipe} # ${nameCol} ${reqCol} ${totalCol} ${inCol} ${outCol} ${pipe}`;\n if (useColor) {\n return colorize(row, \"bright\", true);\n }\n return row;\n }\n\n function tableRow(index, row) {\n const nameCol = padRight(row.name, nameWidth);\n const reqCol = padLeft(formatNumber(row.requests), colWidths.requests);\n const totalCol = padLeft(formatNumber(row.total), colWidths.total);\n const inCol = padLeft(formatNumber(row.input), colWidths.input);\n const outCol = padLeft(formatNumber(row.output), colWidths.output);\n const line = `${pipe} ${index} ${nameCol} ${reqCol} ${totalCol} ${inCol} ${outCol} ${pipe}`;\n if (useColor) {\n return colorize(line, \"cyan\", true);\n }\n return line;\n }\n\n const lines = [];\n lines.push(horizontalBar(topLeft, border, topRight));\n for (const section of sections) {\n const labelText = colorize(section.label, \"bright\", useColor);\n const valueText = useColor ? colorize(section.value, \"dim\", true) : section.value;\n lines.push(`${pipe} ${labelText}: ${valueText}`);\n }\n\n lines.push(sectionDivider(\"Providers\"));\n if (providers.rows.length === 0) {\n lines.push(`${pipe} ${colorize(\"none\", \"dim\", useColor)}`);\n } else {\n lines.push(tableHeader());\n providers.rows.forEach((row, i) => {\n lines.push(tableRow(i + 1, row));\n });\n }\n\n lines.push(sectionDivider(\"Models\"));\n if (models.rows.length === 0) {\n lines.push(`${pipe} ${colorize(\"none\", \"dim\", useColor)}`);\n } else {\n if (providers.rows.length === 0) lines.push(tableHeader());\n models.rows.forEach((row, i) => {\n lines.push(tableRow(i + 1, row));\n });\n }\n\n lines.push(horizontalBar(botLeft, border, botRight));\n return lines.join(\"\\n\");\n}\n\nasync function fetchDbLayerStats(fetchFn, baseUrl) {\n const response = await fetchFn(`${String(baseUrl).replace(/\\/$/, \"\")}/metering/stats`);\n if (!response.ok) {\n throw new Error(`db-layer stats HTTP ${response.status}`);\n }\n return response.json();\n}\n\nfunction resolveDbLayerUrlForEnv(env = {}) {\n const explicit = String(env.DBLAYER_URL || \"\").trim();\n if (explicit) return explicit.replace(/\\/$/, \"\");\n const profile = resolveRuntimeProfile({ env });\n if (profile === \"staging\") return \"http://localhost:6001\";\n if (profile === \"production\") return \"http://localhost:7001\";\n return \"http://localhost:5001\";\n}\n\nfunction resolveEventBusUrlForEnv(env = {}) {\n const explicit = String(env.EVENTBUS_URL || \"\").trim();\n const profile = resolveRuntimeProfile({ env });\n const expectedPort = profile === \"production\" ? \"7048\" : profile === \"staging\" ? \"6048\" : \"5048\";\n if (explicit) return resolveServiceUrlForProfile(explicit.replace(/\\/$/, \"\"), expectedPort);\n return `http://localhost:${expectedPort}`;\n}\n\nasync function loadMeteringStats({ env, paths, fetchFn }) {\n const fallbackSink = createJsonlMeteringSink({ filePath: paths.meteringFile });\n const mode = String(env.LLMPROXY_MODE || \"standalone\").trim().toLowerCase();\n const mongoConnectionString = String(env.LLMPROXY_MONGODB_CONNECTION_STRING || \"\").trim();\n\n if (mode === \"platform\") {\n try {\n const stats = await fetchDbLayerStats(fetchFn, resolveDbLayerUrlForEnv(env));\n return { stats, source: \"dblayer\" };\n } catch {\n return { stats: await Promise.resolve(fallbackSink.computeStats({})), source: \"local-fallback\" };\n }\n }\n\n if (mongoConnectionString) {\n const { createMongoMeteringSink } = require(\"./metering-db\");\n const sink = createMongoMeteringSink({\n uri: mongoConnectionString,\n collectionName: \"llmproxy_metering\",\n });\n try {\n const stats = await Promise.resolve(sink.computeStats({}));\n return { stats, source: \"mongodb\" };\n } finally {\n await Promise.resolve(sink.close()).catch(() => {});\n }\n }\n\n return { stats: await Promise.resolve(fallbackSink.computeStats({})), source: \"jsonl\" };\n}\n\nfunction formatStatsReport(stats = {}, source = \"unknown\", useColor = false) {\n return renderStatsTable(stats, source, useColor);\n}\n\nfunction sanitizeTestAssistantText(text) {\n return String(text || \"\")\n .split(\"\\n\")\n .map((line) => line.trimEnd())\n .filter((line) => !line.startsWith(\"[llmproxy] \"))\n .join(\"\\n\")\n .trim();\n}\n\nfunction requestSummaryMatchesProvider(entry, provider) {\n const finalProvider = String(entry?.finalProvider || \"\").trim().toLowerCase();\n if (!finalProvider) return false;\n\n const providerId = String(provider?.id || \"\").trim().toLowerCase();\n const providerKind = String(provider?.provider || \"\").trim().toLowerCase();\n const providerMatch = finalProvider === providerId || (providerKind && finalProvider === providerKind);\n if (!providerMatch) return false;\n\n // Evita un summary stale di un'altra richiesta risolta dallo stesso\n // finalProvider (es. fallback): il modello richiesto deve corrispondere\n // al modello che il probe stava realmente inviando.\n const entryRequestedModel = String(entry?.requestedModel || \"\").trim().toLowerCase();\n if (entryRequestedModel) {\n const providerModel = String(provider?.effective_model || provider?.default_model || \"\").trim().toLowerCase();\n if (providerModel && entryRequestedModel !== providerModel) return false;\n }\n return true;\n}\n\nfunction decodeBase64Utf8(value) {\n const raw = String(value || \"\").trim();\n if (!raw) return \"\";\n try {\n return Buffer.from(raw, \"base64\").toString(\"utf8\");\n } catch {\n return \"\";\n }\n}\n\nconst LLM_STATS_API_KEY_REQUIRED_MESSAGE = [\n \"LLMPROXY_LLM_STATS_API_KEY is mandatory for llmProxy Claude Code inference.\",\n \"\",\n \"Get a free API key from https://llm-stats.com/developer\",\n \"Then set it in .claude/settings.json under env.LLMPROXY_LLM_STATS_API_KEY and retry.\",\n \"\",\n \"Example:\",\n \"{\",\n ' \"env\": {',\n ' \"LLMPROXY_LLM_STATS_API_KEY\": \"your-free-key\"',\n \" }\",\n \"}\",\n].join(\"\\n\");\n\nfunction parseCommitMessageReleaseNotes(commitMessage) {\n return String(commitMessage || \"\")\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => line.replace(/^[-*]\\s*/, \"\").trim())\n .filter(Boolean);\n}\n\nfunction formatReleaseNotes(version, locale = \"it\", options = {}) {\n const normalizedLocale = normalizeLocale(locale);\n const notesFromCommit = parseCommitMessageReleaseNotes(options.commitMessage);\n const notesFromPackage = readEmbeddedReleaseNotes(options.packageRoot, version, normalizedLocale);\n const value = RELEASE_NOTES[String(version || \"\").trim()];\n const fallbackNotes = Array.isArray(value)\n ? value\n : Array.isArray(value?.[normalizedLocale])\n ? value[normalizedLocale]\n : [];\n const notes = notesFromCommit.length > 0\n ? notesFromCommit\n : notesFromPackage.length > 0\n ? notesFromPackage\n : fallbackNotes;\n\n const title = normalizedLocale === \"en\" ? `Changelog ${version}:` : `Changelog ${version}:`;\n const lines = [title];\n if (!notes.length) {\n lines.push(normalizedLocale === \"en\"\n ? \"- Release notes are not available for this version yet.\"\n : \"- Note di rilascio non disponibili per questa versione.\");\n return lines.join(\"\\n\");\n }\n for (const note of notes) lines.push(`- ${note}`);\n return lines.join(\"\\n\");\n}\n\nfunction resolveDefaultModel(selection, availableModels = getAvailableModels()) {\n const models = Array.isArray(availableModels) && availableModels.length > 0 ? availableModels : getAvailableModels();\n const rawSelection = String(selection || \"\").trim();\n if (!rawSelection) return DEFAULT_COPILOT_MODEL;\n\n if (!/^\\d+$/.test(rawSelection)) {\n throw new Error(\"Usa l'indice numerico di `llmproxy models:list` con `llmproxy claude:setup --model `.\");\n }\n\n const index = Number(rawSelection) - 1;\n if (index < 0 || index >= models.length) {\n throw new Error(`Indice modello non valido: ${rawSelection}`);\n }\n return models[index];\n}\n\nfunction runSelfUpdate(commandRunner = spawnSync, repo = \"alessiobacin/llmProxy\", locale = \"it\") {\n const script = [\n \"set -e\",\n \"tmpdir=$(mktemp -d)\",\n \"cleanup() { rm -rf \\\"$tmpdir\\\"; }\",\n \"trap cleanup EXIT\",\n \"\",\n \"# Preserve the caller's current global npm bin before any nvm switch\",\n 'original_npm_prefix=$(npm prefix -g 2>/dev/null || true)',\n 'original_global_bin_path=\"\"',\n 'original_global_short_bin_path=\"\"',\n 'if [ -n \"$original_npm_prefix\" ]; then',\n ' original_global_bin_path=\"$original_npm_prefix/bin/llmproxy\"',\n ' original_global_short_bin_path=\"$original_npm_prefix/bin/llmp\"',\n 'fi',\n \"\",\n \"# Load nvm if available to use the correct Node version\",\n 'if [ -f \"$HOME/.nvm/nvm.sh\" ]; then',\n ' source \"$HOME/.nvm/nvm.sh\" 2>/dev/null || true',\n 'elif [ -f \"/usr/share/nvm/init-nvm.sh\" ]; then',\n ' source \"/usr/share/nvm/init-nvm.sh\" 2>/dev/null || true',\n 'fi',\n \"\",\n \"# Try to use a modern Node version (20+) if available\",\n 'if command -v nvm >/dev/null 2>&1; then',\n ' nvm use 22 >/dev/null 2>&1 || nvm use 20 >/dev/null 2>&1 || nvm use lts >/dev/null 2>&1 || true',\n 'fi',\n \"\",\n \"# Verify Node version is at least 20\",\n 'node_version=$(node -v 2>/dev/null | sed \"s/v//\" | cut -d. -f1)',\n 'if [ -n \"$node_version\" ] && [ \"$node_version\" -lt 20 ]; then',\n ' echo \"Warning: Node v$(node -v) may be too old. Attempting to use a newer version...\"',\n 'fi',\n \"\",\n \"existing_bins=$( (which -a llmproxy 2>/dev/null; which -a llmp 2>/dev/null) | awk '!seen[$0]++')\",\n \"current_bin=$(command -v llmproxy 2>/dev/null || true)\",\n \"current_short_bin=$(command -v llmp 2>/dev/null || true)\",\n 'current_version=$(llmproxy version 2>/dev/null || true)',\n 'original_npm_root=$(npm root -g 2>/dev/null || true)',\n 'current_bin_prefix=\"\"',\n 'if [ -n \"$current_bin\" ]; then',\n ' current_bin_dir=$(dirname \"$current_bin\")',\n ' current_bin_prefix=$(cd \"$current_bin_dir/..\" 2>/dev/null && pwd || true)',\n 'fi',\n \"used_sudo=0\",\n 'rollback_manifest=\"$tmpdir/rollback-manifest.txt\"',\n 'touch \"$rollback_manifest\"',\n 'rollback_primary_dir=\"\"',\n 'backup_install_dir() {',\n ' src=\"$1\"',\n ' [ -d \"$src\" ] || return 0',\n ' archive=\"$tmpdir/rollback-$(wc -l < \"$rollback_manifest\" | tr -d \" \")-$(basename \"$src\").tar\"',\n ' tar -cf \"$archive\" -C \"$(dirname \"$src\")\" \"$(basename \"$src\")\"',\n ' printf \"%s|%s\\\\n\" \"$src\" \"$archive\" >> \"$rollback_manifest\"',\n ' if [ -z \"$rollback_primary_dir\" ]; then',\n ' rollback_primary_dir=\"$src\"',\n ' fi',\n '}',\n 'restore_backups() {',\n ' if [ ! -s \"$rollback_manifest\" ]; then',\n ' return 1',\n ' fi',\n ' while IFS=\"|\" read -r target archive; do',\n ' [ -n \"$target\" ] || continue',\n ' parent_dir=$(dirname \"$target\")',\n ' rm -rf \"$target\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo rm -rf \"$target\" >/dev/null 2>&1 || return 1; else return 1; fi; }',\n ' mkdir -p \"$parent_dir\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo mkdir -p \"$parent_dir\" >/dev/null 2>&1 || return 1; else return 1; fi; }',\n ' tar -xf \"$archive\" -C \"$parent_dir\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo tar -xf \"$archive\" -C \"$parent_dir\" >/dev/null 2>&1 || return 1; else return 1; fi; }',\n ' done < \"$rollback_manifest\"',\n ' return 0',\n '}',\n 'rollback_and_exit() {',\n ' rollback_reason=\"$1\"',\n ' restored_version=\"$current_version\"',\n ' if restore_backups; then',\n ' if [ -n \"$rollback_primary_dir\" ] && [ -f \"$rollback_primary_dir/bin/llmproxy.js\" ]; then',\n ' package_cli=\"$rollback_primary_dir/bin/llmproxy.js\"',\n ' new_bin=\"$package_cli\"',\n ' new_bin_mode=\"node\"',\n ' if type ensure_wrapper_path >/dev/null 2>&1; then',\n ' ensure_wrapper_path \"${global_bin_path:-}\" >/dev/null 2>&1 || true',\n ' for preserved_bin in ${preserved_bin_paths:-}; do',\n ' ensure_wrapper_path \"$preserved_bin\" >/dev/null 2>&1 || true',\n ' done',\n ' fi',\n ' if [ \"$used_sudo\" -eq 1 ] && [ \"$(uname -s)\" = \"Linux\" ] && [ -n \"${SUDO_USER:-}\" ] && command -v sudo >/dev/null 2>&1; then',\n ' 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\" node \"$package_cli\" service:restart >/dev/null 2>&1 || true',\n ' else',\n ' node \"$package_cli\" service:restart >/dev/null 2>&1 || true',\n ' fi',\n ' restored_version=$(node \"$package_cli\" version 2>/dev/null || printf \"%s\" \"$current_version\")',\n ' fi',\n ' fi',\n ' printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"',\n ' printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"$restored_version\"',\n ' printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"$rollback_reason\"',\n ' exit 0',\n '}',\n 'backup_install_dir \"${original_npm_root:+$original_npm_root/llmproxy}\"',\n 'backup_install_dir \"${current_bin_prefix:+$current_bin_prefix/lib/node_modules/llmproxy}\"',\n 'safe_remove_path() {',\n ' target_path=\"$1\"',\n ' [ -n \"$target_path\" ] || return 0',\n ' if [ -e \"$target_path\" ] || [ -L \"$target_path\" ]; then',\n ' rm -rf \"$target_path\" >/dev/null 2>&1 || {',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo rm -rf \"$target_path\" >/dev/null 2>&1 || true',\n ' fi',\n ' }',\n ' fi',\n '}',\n 'collect_install_target() {',\n ' candidate_dir=\"$1\"',\n ' [ -n \"$candidate_dir\" ] || return 0',\n ' [ -d \"$candidate_dir\" ] || return 0',\n ' printf \"%s\\\\n\" \"$candidate_dir\"',\n '}',\n 'purge_installation_targets() {',\n ' install_targets=$( {',\n ' collect_install_target \"${original_npm_root:+$original_npm_root/llmproxy}\"',\n ' collect_install_target \"${current_bin_prefix:+$current_bin_prefix/lib/node_modules/llmproxy}\"',\n ' for discovered_bin in $existing_bins; do',\n ' [ -n \"$discovered_bin\" ] || continue',\n ' discovered_prefix=$(cd \"$(dirname \"$discovered_bin\")/..\" 2>/dev/null && pwd || true)',\n ' collect_install_target \"${discovered_prefix:+$discovered_prefix/lib/node_modules/llmproxy}\"',\n ' done',\n ' current_npm_root=$(npm root -g 2>/dev/null || true)',\n ' collect_install_target \"${current_npm_root:+$current_npm_root/llmproxy}\"',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo_npm_root=$(sudo npm root -g 2>/dev/null || true)',\n ' collect_install_target \"${sudo_npm_root:+$sudo_npm_root/llmproxy}\"',\n ' fi',\n ' pnpm_root=$(pnpm root -g 2>/dev/null || true)',\n ' collect_install_target \"${pnpm_root:+$pnpm_root/llmproxy}\"',\n ' collect_install_target \"/usr/lib/node_modules/llmproxy\"',\n ' collect_install_target \"/usr/local/lib/node_modules/llmproxy\"',\n ' collect_install_target \"/opt/homebrew/lib/node_modules/llmproxy\"',\n ' } | awk \\'NF && !seen[$0]++\\' )',\n ' for install_target in $install_targets; do',\n ' backup_install_dir \"$install_target\"',\n ' safe_remove_path \"$install_target\"',\n ' done',\n '}',\n 'purge_existing_wrappers() {',\n ' 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 ' for wrapper_target in $wrapper_targets; do',\n ' safe_remove_path \"$wrapper_target\"',\n ' done',\n '}',\n 'cleanup_global_service_port() {',\n ' service_port=\"${1:-7045}\"',\n ' [ -n \"$service_port\" ] || return 0',\n ' if command -v docker >/dev/null 2>&1; then',\n ' docker_ids=$(docker ps --format \\'{{.ID}} {{.Ports}}\\' 2>/dev/null | awk -v port=\"$service_port\" \\'index($0, \":\" port \"->\") > 0 { print $1 }\\')',\n ' if [ -n \"$docker_ids\" ]; then',\n ' for docker_id in $docker_ids; do',\n ' docker stop \"$docker_id\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo docker stop \"$docker_id\" >/dev/null 2>&1 || true; else true; fi; }',\n ' docker rm \"$docker_id\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo docker rm \"$docker_id\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' fi',\n ' fi',\n ' if command -v lsof >/dev/null 2>&1; then',\n ' listener_pids=$(lsof -tiTCP:\"$service_port\" -sTCP:LISTEN 2>/dev/null || true)',\n ' if [ -n \"$listener_pids\" ]; then',\n ' for listener_pid in $listener_pids; do',\n ' kill \"$listener_pid\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo kill \"$listener_pid\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' sleep 1',\n ' listener_pids=$(lsof -tiTCP:\"$service_port\" -sTCP:LISTEN 2>/dev/null || true)',\n ' if [ -n \"$listener_pids\" ]; then',\n ' for listener_pid in $listener_pids; do',\n ' kill -9 \"$listener_pid\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo kill -9 \"$listener_pid\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' fi',\n ' fi',\n ' fi',\n '}',\n 'ensure_runtime_home_permissions() {',\n ' runtime_home=\"$1\"',\n ' runtime_owner=\"$2\"',\n ' [ -n \"$runtime_home\" ] || return 0',\n ' mkdir -p \"$runtime_home/logs\" >/dev/null 2>&1 || {',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo mkdir -p \"$runtime_home/logs\" >/dev/null 2>&1 || true',\n ' fi',\n ' }',\n ' touch \"$runtime_home/logs/service.out.log\" \"$runtime_home/logs/service.err.log\" \"$runtime_home/logs/metering.jsonl\" >/dev/null 2>&1 || {',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo touch \"$runtime_home/logs/service.out.log\" \"$runtime_home/logs/service.err.log\" \"$runtime_home/logs/metering.jsonl\" >/dev/null 2>&1 || true',\n ' fi',\n ' }',\n ' if [ -n \"$runtime_owner\" ] && command -v sudo >/dev/null 2>&1; then',\n ' sudo chown -R \"$runtime_owner\":\"$runtime_owner\" \"$runtime_home\" >/dev/null 2>&1 || true',\n ' fi',\n ' chmod -R u+rwX \"$runtime_home\" >/dev/null 2>&1 || true',\n '}',\n 'cleanup_global_service_port \"${PORT:-7045}\"',\n 'npm uninstall -g llmproxy >/dev/null 2>&1 || true',\n 'if command -v sudo >/dev/null 2>&1; then',\n ' sudo npm uninstall -g llmproxy >/dev/null 2>&1 || true',\n 'fi',\n 'pnpm remove -g llmproxy >/dev/null 2>&1 || true',\n 'purge_installation_targets',\n 'purge_existing_wrappers',\n `git clone --depth=1 https://github.com/${repo}.git \\\"$tmpdir/repo\\\" >/dev/null 2>&1`,\n 'cd \"$tmpdir/repo\"',\n 'target_version=$(node -p \"require(\\'./package.json\\').version\")',\n \"commit_message_base64=$(git log -1 --pretty=%B | base64 | tr -d '\\\\n')\",\n \"\",\n \"# Try pnpm pack first, fall back to npm pack\",\n 'if command -v pnpm >/dev/null 2>&1 && pnpm pack --pack-destination \"$tmpdir\" >/dev/null 2>&1; then',\n ' package_file=$(find \"$tmpdir\" -maxdepth 1 -name \"*.tgz\" -print | head -n 1)',\n 'else',\n ' npm pack --pack-destination \"$tmpdir\" >/dev/null 2>&1',\n ' package_file=$(find \"$tmpdir\" -maxdepth 1 -name \"*.tgz\" -print | head -n 1)',\n 'fi',\n '[ -n \"$package_file\" ]',\n \"\",\n '# Check npm prefix: if user-local, force system-wide install via sudo for multi-user server',\n 'npm_prefix_global=$(npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'case \"$npm_prefix_global\" in',\n ' \"$HOME\"/*)',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' if sudo npm install -g --force \"$package_file\" 2>/dev/null; then',\n ' used_sudo=1',\n ' else',\n ' echo \"Warning: could not install system-wide. Falling back to user-local.\" >&2',\n ' if ! npm install -g --force \"$package_file\" 2>/dev/null; then',\n ' rollback_and_exit \"npm install failed while applying $target_version\"',\n ' fi',\n ' fi',\n ' else',\n ' if ! npm install -g --force \"$package_file\" 2>/dev/null; then',\n ' rollback_and_exit \"npm install failed while applying $target_version\"',\n ' fi',\n ' fi',\n ' ;;',\n ' *)',\n ' if ! npm install -g --force \"$package_file\" 2>/dev/null; then',\n ' echo \"Standard npm install failed, trying with sudo...\"',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' if sudo npm install -g --force \"$package_file\" 2>/dev/null; then',\n ' used_sudo=1',\n ' else',\n ' rollback_and_exit \"npm install failed while applying $target_version\"',\n ' fi',\n ' else',\n ' rollback_and_exit \"npm install failed while applying $target_version\"',\n ' fi',\n ' fi',\n ' ;;',\n 'esac',\n \"\",\n \"# Clean up pnpm global if present\",\n 'pnpm remove -g llmproxy >/dev/null 2>&1 || true',\n 'pnpm_root=$(pnpm root -g 2>/dev/null || true)',\n 'if [ -n \"$pnpm_root\" ]; then',\n ' pnpm_home=$(dirname \"$(dirname \"$pnpm_root\")\")',\n ' rm -f \"$pnpm_home/bin/llmproxy\" >/dev/null 2>&1 || true',\n 'fi',\n \"\",\n \"# Determine npm global prefix\",\n 'if [ \"$used_sudo\" -eq 1 ]; then',\n ' npm_prefix=$(sudo npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'else',\n ' npm_prefix=$(npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'fi',\n 'install_dir=\"$npm_prefix/lib/node_modules/llmproxy\"',\n 'package_cli=\"$install_dir/bin/llmproxy.js\"',\n 'global_bin_path=\"$npm_prefix/bin/llmproxy\"',\n 'global_short_bin_path=\"$npm_prefix/bin/llmp\"',\n 'new_bin_mode=\"bin\"',\n 'run_new_llmproxy() {',\n ' if [ \"$new_bin_mode\" = \"node\" ] && [ -f \"$new_bin\" ]; then',\n ' node \"$new_bin\" \"$@\"',\n ' elif [ -x \"$new_bin\" ]; then',\n ' \"$new_bin\" \"$@\"',\n ' elif [ -f \"$package_cli\" ]; then',\n ' node \"$package_cli\" \"$@\"',\n ' else',\n ' return 127',\n ' fi',\n '}',\n 'build_wrapper_payload() {',\n ' if [ \"$new_bin_mode\" = \"node\" ] && [ -f \"$package_cli\" ]; then',\n ' printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec node \\\\\"$package_cli\\\\\" \\\\\"\\\\$@\\\\\"\"',\n ' return 0',\n ' fi',\n ' if [ -n \"$new_bin\" ] && [ -e \"$new_bin\" ]; then',\n ' printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec \\\\\"$new_bin\\\\\" \\\\\"\\\\$@\\\\\"\"',\n ' return 0',\n ' fi',\n ' return 1',\n '}',\n 'ensure_wrapper_path() {',\n ' target_path=\"$1\"',\n ' if [ -z \"$target_path\" ] || [ \"$target_path\" = \"$new_bin\" ]; then',\n ' return 0',\n ' fi',\n ' target_dir=$(dirname \"$target_path\")',\n ' if [ ! -d \"$target_dir\" ]; then',\n ' return 0',\n ' fi',\n ' wrapper_payload=$(build_wrapper_payload) || return 1',\n ' if [ -w \"$target_dir\" ]; then',\n ' rm -f \"$target_path\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$wrapper_payload\" > \"$target_path\"',\n ' chmod +x \"$target_path\"',\n ' return 0',\n ' fi',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo rm -f \"$target_path\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$wrapper_payload\" | sudo tee \"$target_path\" >/dev/null',\n ' sudo chmod +x \"$target_path\"',\n ' return 0',\n ' fi',\n ' return 1',\n '}',\n 'ensure_global_bin() {',\n ' if [ -x \"$global_bin_path\" ]; then',\n ' return 0',\n ' fi',\n ' ensure_wrapper_path \"$global_bin_path\"',\n '}',\n 'ensure_global_short_bin() {',\n ' if [ -x \"$global_short_bin_path\" ]; then',\n ' return 0',\n ' fi',\n ' ensure_wrapper_path \"$global_short_bin_path\"',\n '}',\n \"\",\n \"# System-wide install: make installed package world-readable (keep root ownership)\",\n 'if [ \"$used_sudo\" -eq 1 ]; then',\n ' if [ -d \"$install_dir\" ]; then',\n ' sudo chmod -R a+rX \"$install_dir\" 2>/dev/null || echo \"Warning: could not make $install_dir world-readable. Non-root users may not be able to use llmproxy.\" >&2',\n ' fi',\n 'fi',\n \"\",\n \"# Find the newly installed binary\",\n \"resolved_bins=$( (which -a llmproxy 2>/dev/null; which -a llmp 2>/dev/null) | awk '!seen[$0]++')\",\n 'new_bin=\"\"',\n 'version_output=\"\"',\n 'if [ -f \"$package_cli\" ]; then',\n ' package_cli_version=$(node \"$package_cli\" version 2>/dev/null || true)',\n ' if [ \"$package_cli_version\" = \"$target_version\" ]; then',\n ' new_bin=\"$package_cli\"',\n ' new_bin_mode=\"node\"',\n ' version_output=\"$package_cli_version\"',\n ' fi',\n 'fi',\n 'if [ \"$version_output\" != \"$target_version\" ] && [ -x \"$global_bin_path\" ]; then',\n ' global_bin_version=$(\"$global_bin_path\" version 2>/dev/null || true)',\n ' if [ \"$global_bin_version\" = \"$target_version\" ]; then',\n ' new_bin=\"$global_bin_path\"',\n ' new_bin_mode=\"bin\"',\n ' version_output=\"$global_bin_version\"',\n ' fi',\n 'fi',\n 'if [ \"$version_output\" != \"$target_version\" ]; then',\n ' for candidate_bin in $resolved_bins; do',\n ' if [ -x \"$candidate_bin\" ]; then',\n ' candidate_version=$(\"$candidate_bin\" version 2>/dev/null || true)',\n ' if [ \"$candidate_version\" = \"$target_version\" ]; then',\n ' new_bin=\"$candidate_bin\"',\n ' new_bin_mode=\"bin\"',\n ' version_output=\"$candidate_version\"',\n ' break',\n ' fi',\n ' fi',\n ' done',\n 'fi',\n 'if [ \"$version_output\" != \"$target_version\" ]; then',\n ' rollback_and_exit \"installed binary verification failed for $target_version\"',\n 'fi',\n 'ensure_global_bin >/dev/null 2>&1 || true',\n 'ensure_global_short_bin >/dev/null 2>&1 || true',\n '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 'for preserved_bin in $preserved_bin_paths; do',\n ' ensure_wrapper_path \"$preserved_bin\" >/dev/null 2>&1 || true',\n 'done',\n 'is_preserved_bin_path() {',\n ' candidate_path=\"$1\"',\n ' for preserved_bin in $preserved_bin_paths; do',\n ' if [ \"$preserved_bin\" = \"$candidate_path\" ]; then',\n ' return 0',\n ' fi',\n ' done',\n ' return 1',\n '}',\n \"\",\n \"# Remove old duplicate binaries\",\n 'for installed_bin in $resolved_bins; do',\n ' if [ -n \"$installed_bin\" ] && [ \"$installed_bin\" != \"$new_bin\" ] && ! is_preserved_bin_path \"$installed_bin\"; then',\n ' rm -f \"$installed_bin\" >/dev/null 2>&1 || { if [ \"$used_sudo\" -eq 1 ]; then sudo rm -f \"$installed_bin\" >/dev/null 2>&1 || true; else true; fi; }',\n ' fi',\n 'done',\n \"\",\n \"# Update current bin if it points to an old location\",\n 'if [ -n \"$current_bin\" ] && [ \"$current_bin\" != \"$new_bin\" ] && [ \"$current_bin\" != \"$global_bin_path\" ]; then',\n ' current_bin_dir=$(dirname \"$current_bin\")',\n ' if [ \"$new_bin_mode\" = \"node\" ]; then',\n ' current_wrapper_payload=$(printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec node \\\\\"$new_bin\\\\\" \\\\\"\\\\$@\\\\\"\" )',\n ' else',\n ' current_wrapper_payload=$(printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec \\\\\"$new_bin\\\\\" \\\\\"\\\\$@\\\\\"\" )',\n ' fi',\n ' if [ -w \"$current_bin_dir\" ]; then',\n ' rm -f \"$current_bin\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$current_wrapper_payload\" > \"$current_bin\"',\n ' chmod +x \"$current_bin\"',\n ' elif command -v sudo >/dev/null 2>&1; then',\n ' sudo rm -f \"$current_bin\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$current_wrapper_payload\" | sudo tee \"$current_bin\" >/dev/null',\n ' sudo chmod +x \"$current_bin\"',\n ' fi',\n 'fi',\n 'if [ -n \"$current_short_bin\" ] && [ \"$current_short_bin\" != \"$new_bin\" ] && [ \"$current_short_bin\" != \"$global_short_bin_path\" ]; then',\n ' current_short_bin_dir=$(dirname \"$current_short_bin\")',\n ' if [ \"$new_bin_mode\" = \"node\" ]; then',\n ' current_short_wrapper_payload=$(printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec node \\\\\"$new_bin\\\\\" \\\\\"\\\\$@\\\\\"\" )',\n ' else',\n ' current_short_wrapper_payload=$(printf \\'%s\\\\n\\' \\'#!/bin/sh\\' \"exec \\\\\"$new_bin\\\\\" \\\\\"\\\\$@\\\\\"\" )',\n ' fi',\n ' if [ -w \"$current_short_bin_dir\" ]; then',\n ' rm -f \"$current_short_bin\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$current_short_wrapper_payload\" > \"$current_short_bin\"',\n ' chmod +x \"$current_short_bin\"',\n ' elif command -v sudo >/dev/null 2>&1; then',\n ' sudo rm -f \"$current_short_bin\" >/dev/null 2>&1 || true',\n ' printf \"%s\\\\n\" \"$current_short_wrapper_payload\" | sudo tee \"$current_short_bin\" >/dev/null',\n ' sudo chmod +x \"$current_short_bin\"',\n ' fi',\n 'fi',\n \"\",\n \"# Ensure systemd --user bus is available before restarting\",\n 'if [ \"$(uname -s)\" = \"Linux\" ] && [ -z \"${XDG_RUNTIME_DIR:-}\" ]; then',\n ' uid=$(id -u)',\n ' export XDG_RUNTIME_DIR=\"/run/user/$uid\"',\n 'fi',\n 'if [ \"$(uname -s)\" = \"Linux\" ] && [ -d \"$XDG_RUNTIME_DIR\" ]; then',\n ' bus_socket=\"$XDG_RUNTIME_DIR/bus\"',\n ' if [ ! -S \"$bus_socket\" ] && command -v systemctl >/dev/null 2>&1; then',\n ' systemctl --user start dbus 2>/dev/null || true',\n ' waited=0',\n ' while [ ! -S \"$bus_socket\" ] && [ \"$waited\" -lt 5 ]; do',\n ' sleep 1',\n ' waited=$((waited + 1))',\n ' done',\n ' fi',\n 'fi',\n \"\",\n 'cleanup_global_service_port \"${PORT:-7045}\"',\n \"\",\n \"# Migrate legacy managed config variables to the current schema\",\n 'run_new_llmproxy config:migrate >/dev/null 2>&1 || true',\n \"\",\n \"# Restart service — use sudo -u when npm install used sudo (systemd --user needs real user session bus)\",\n 'if [ \"$used_sudo\" -eq 1 ] && [ \"$(uname -s)\" = \"Linux\" ] && [ -n \"${SUDO_USER:-}\" ] && command -v sudo >/dev/null 2>&1; then',\n ' if [ \"$new_bin_mode\" = \"node\" ]; then',\n ' service_restart_output=$(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\" node \"$new_bin\" service:restart 2>&1 >/dev/null) || service_restart_status=$?',\n ' else',\n ' service_restart_output=$(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\" \"$new_bin\" service:restart 2>&1 >/dev/null) || service_restart_status=$?',\n ' fi',\n 'else',\n ' service_restart_output=$(run_new_llmproxy service:restart 2>&1 >/dev/null) || service_restart_status=$?',\n 'fi',\n \"\",\n \"# Update Docker container if running\",\n 'docker_compose_file=\"$npm_prefix/lib/node_modules/llmproxy/docker-compose.production.yml\"',\n 'if [ -z \"$LLMPROXY_HOME\" ]; then',\n ' if [ \"$(uname -s)\" = \"Darwin\" ]; then',\n ' export LLMPROXY_HOME=\"$HOME/Library/Application Support/llmProxy\"',\n ' else',\n ' export LLMPROXY_HOME=\"$HOME/.local/share/llmProxy\"',\n ' fi',\n 'fi',\n 'if [ -z \"$LLMPROXY_HOST_PROJECTS_ROOT\" ]; then',\n ' if [ \"$(uname -s)\" = \"Darwin\" ]; then',\n ' export LLMPROXY_HOST_PROJECTS_ROOT=\"/Users\"',\n ' else',\n ' export LLMPROXY_HOST_PROJECTS_ROOT=\"/home\"',\n ' fi',\n 'fi',\n 'runtime_owner=\"\"',\n 'if [ \"$used_sudo\" -eq 1 ] && [ -n \"${SUDO_USER:-}\" ]; then',\n ' runtime_owner=\"$SUDO_USER\"',\n 'else',\n ' runtime_owner=\"$(whoami)\"',\n 'fi',\n 'ensure_runtime_home_permissions \"$LLMPROXY_HOME\" \"$runtime_owner\"',\n 'compose_cmd=\"\"',\n 'compose_args=\"\"',\n 'if command -v docker >/dev/null 2>&1 && [ -f \"$docker_compose_file\" ]; then',\n ' if docker compose version >/dev/null 2>&1; then',\n ' compose_cmd=\"docker\"',\n ' compose_args=\"compose\"',\n ' elif command -v docker-compose >/dev/null 2>&1 && docker-compose version >/dev/null 2>&1; then',\n ' compose_cmd=\"docker-compose\"',\n ' fi',\n ' if [ -n \"$compose_cmd\" ]; then',\n ' if [ \"$compose_cmd\" = \"docker\" ]; then',\n ' if docker compose -f \"$docker_compose_file\" ps --services --status running 2>/dev/null | grep -qx \"llmproxy\"; then',\n ' docker compose -f \"$docker_compose_file\" up -d --build llmproxy >/dev/null || true',\n ' fi',\n ' else',\n ' if docker-compose -f \"$docker_compose_file\" ps --services --status running 2>/dev/null | grep -qx \"llmproxy\"; then',\n ' docker-compose -f \"$docker_compose_file\" up -d --build llmproxy >/dev/null || true',\n ' fi',\n ' fi',\n ' fi',\n 'fi',\n \"\",\n \"# Smoke test the freshly installed CLI and managed runtime before declaring success\",\n 'post_update_check() {',\n ' run_new_llmproxy version >/dev/null 2>&1 || return 1',\n ' run_new_llmproxy config:list >/dev/null 2>&1 || return 1',\n ' attempts=0',\n ' while [ \"$attempts\" -lt 5 ]; do',\n ' if run_new_llmproxy status >/dev/null 2>&1; then',\n ' return 0',\n ' fi',\n ' attempts=$((attempts + 1))',\n ' sleep 2',\n ' done',\n ' return 1',\n '}',\n 'if ! post_update_check; then',\n ' rollback_and_exit \"post-update smoke test failed for $target_version\"',\n 'fi',\n \"\",\n \"# Output results\",\n `release_notes_output=$(run_new_llmproxy release-notes --version \"$version_output\" --locale ${shellQuote(normalizeLocale(locale))} --commit-message-base64 \"$commit_message_base64\")`,\n 'printf \"__LLMPROXY_VERSION__=%s\\\\n\" \"$version_output\"',\n 'printf \"__LLMPROXY_RELEASE_NOTES_START__\\\\n%s\\\\n__LLMPROXY_RELEASE_NOTES_END__\\\\n\" \"$release_notes_output\"',\n 'if [ \"$service_restart_status\" -ne 0 ]; then',\n ' printf \"__LLMPROXY_SERVICE_RESTART_WARNING__=%s\\\\n\" \"$service_restart_output\"',\n 'fi',\n ].join(\"\\n\");\n return commandRunner(\"bash\", [\"-c\", script], { encoding: \"utf8\" });\n}\n\nfunction runSelfUninstall(commandRunner = spawnSync) {\n const script = [\n 'set -e',\n 'npm uninstall -g llmproxy >/dev/null 2>&1 || true',\n 'pnpm remove -g llmproxy >/dev/null 2>&1 || true',\n 'npm_prefix=$(npm prefix -g 2>/dev/null || true)',\n 'if [ -n \"$npm_prefix\" ]; then',\n ' rm -f \"$npm_prefix/bin/llmproxy\" >/dev/null 2>&1 || true',\n ' rm -f \"$npm_prefix/bin/llmp\" >/dev/null 2>&1 || true',\n 'fi',\n 'pnpm_root=$(pnpm root -g 2>/dev/null || true)',\n 'if [ -n \"$pnpm_root\" ]; then',\n ' pnpm_home=$(dirname \"$(dirname \"$pnpm_root\")\")',\n ' rm -f \"$pnpm_home/bin/llmproxy\" >/dev/null 2>&1 || true',\n ' rm -f \"$pnpm_home/bin/llmp\" >/dev/null 2>&1 || true',\n 'fi',\n ].join(\"\\n\");\n return commandRunner(\"bash\", [\"-c\", script], { encoding: \"utf8\" });\n}\n\nfunction runSelfUpdateWindows(commandRunner = spawnSync, repo = \"alessiobacin/llmProxy\", locale = \"it\") {\n const psScript = [\n \"$ErrorActionPreference = 'Stop'\",\n 'if ($PSVersionTable.PSVersion.Major -ge 7) { $PSNativeCommandUseErrorActionPreference = $false }',\n '$ProgressPreference = \"SilentlyContinue\"',\n '$tmpdir = [System.IO.Path]::GetTempPath() + [System.IO.Path]::GetRandomFileName()',\n 'New-Item -ItemType Directory -Path $tmpdir -Force | Out-Null',\n '',\n 'function Quote-CmdArgument([string]$Value) {',\n ' if ($null -eq $Value) { return \"\\\"\\\"\" }',\n ' $escaped = $Value -replace \\'\"\\', \\'\"\"\\'',\n ' if ($escaped -match \\'[\\\\s\"&|<>()^]\\') { return \\'\"\\' + $escaped + \\'\"\\' }',\n ' return $escaped',\n '}',\n '',\n 'function Invoke-QuietNative([string]$FilePath, [string[]]$ArgumentList) {',\n ' $stdoutFile = Join-Path $tmpdir ([System.IO.Path]::GetRandomFileName())',\n ' $stderrFile = Join-Path $tmpdir ([System.IO.Path]::GetRandomFileName())',\n ' try {',\n ' $resolvedFilePath = $FilePath',\n ' $resolvedArguments = @($ArgumentList)',\n ' $extension = [System.IO.Path]::GetExtension($FilePath).ToLowerInvariant()',\n ' if ($FilePath -ieq \"npm\" -or $FilePath -ieq \"npm.cmd\" -or $extension -eq \".cmd\" -or $extension -eq \".bat\") {',\n ' $resolvedFilePath = \"cmd.exe\"',\n ' $cmdLine = (@($FilePath) + @($ArgumentList) | ForEach-Object { Quote-CmdArgument $_ }) -join \" \"',\n ' $resolvedArguments = @(\"/d\", \"/s\", \"/c\", $cmdLine)',\n ' } elseif ($extension -eq \".ps1\") {',\n ' $resolvedFilePath = \"powershell.exe\"',\n ' $resolvedArguments = @(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", $FilePath) + @($ArgumentList)',\n ' } elseif ($extension -eq \".js\") {',\n ' $resolvedFilePath = \"node.exe\"',\n ' $resolvedArguments = @($FilePath) + @($ArgumentList)',\n ' }',\n ' $process = Start-Process -FilePath $resolvedFilePath -ArgumentList $resolvedArguments -NoNewWindow -PassThru -Wait -RedirectStandardOutput $stdoutFile -RedirectStandardError $stderrFile',\n ' if ($process.ExitCode -ne 0) {',\n ' $stderrText = if (Test-Path $stderrFile) { (Get-Content $stderrFile -Raw -ErrorAction SilentlyContinue) } else { \"\" }',\n ' $stdoutText = if (Test-Path $stdoutFile) { (Get-Content $stdoutFile -Raw -ErrorAction SilentlyContinue) } else { \"\" }',\n ' throw (\"Native command failed: \" + $resolvedFilePath + \" \" + ($resolvedArguments -join \" \") + \" :: \" + ($stderrText + $stdoutText).Trim())',\n ' }',\n ' } finally {',\n ' Remove-Item -Force $stdoutFile -ErrorAction SilentlyContinue',\n ' Remove-Item -Force $stderrFile -ErrorAction SilentlyContinue',\n ' }',\n '}',\n '',\n '$currentVersion = ((& llmproxy version 2>$null) | Out-String).Trim()',\n '$currentNpmRoot = ((npm root -g 2>$null) | Out-String).Trim()',\n '$currentInstallDir = if ($currentNpmRoot) { Join-Path $currentNpmRoot \"llmproxy\" } else { \"\" }',\n '$rollbackDir = Join-Path $tmpdir \"rollback-llmproxy\"',\n '$rollbackAvailable = $false',\n 'if ($currentInstallDir -and (Test-Path $currentInstallDir)) {',\n ' Copy-Item -Recurse -Force $currentInstallDir $rollbackDir',\n ' $rollbackAvailable = $true',\n '}',\n '',\n `Invoke-QuietNative \"git\" @(\"clone\", \"--depth=1\", \"https://github.com/${repo}.git\", \"$tmpdir/repo\")`,\n 'Set-Location \"$tmpdir/repo\"',\n '',\n '$targetVersion = node -p \"require(\\'./package.json\\').version\"',\n '$commitMessageBytes = [Text.Encoding]::UTF8.GetBytes((git log -1 --pretty=%B 2>$null))',\n '$commitMessageBase64 = [Convert]::ToBase64String($commitMessageBytes)',\n '',\n '# Stop the currently running Windows service before replacing the global package files',\n '& llmproxy service:stop 2>$null | Out-Null',\n '',\n '# npm pack',\n 'Invoke-QuietNative \"npm\" @(\"pack\", \"--pack-destination\", \"$tmpdir\")',\n '$packageFile = Get-ChildItem \"$tmpdir/*.tgz\" | Select-Object -First 1 -ExpandProperty FullName',\n '',\n '# Install globally (always same user on Windows)',\n 'Invoke-QuietNative \"npm\" @(\"install\", \"-g\", \"--force\", \"$packageFile\")',\n '',\n '# Clean up pnpm global if present',\n 'pnpm remove -g llmproxy 2>$null | Out-Null',\n '$pnpmRoot = pnpm root -g 2>$null',\n 'if ($pnpmRoot) {',\n ' $pnpmHome = Split-Path (Split-Path $pnpmRoot) -Parent',\n ' Remove-Item -Force \"$pnpmHome/bin/llmproxy\" -ErrorAction SilentlyContinue',\n '}',\n '',\n 'function Resolve-LlmproxyGlobalBin([string]$Prefix) {',\n ' $candidates = @(',\n ' (Join-Path $Prefix \"llmproxy.cmd\"),',\n ' (Join-Path $Prefix \"llmproxy.ps1\"),',\n ' (Join-Path $Prefix \"llmproxy\"),',\n ' (Join-Path $Prefix \"llmp.cmd\"),',\n ' (Join-Path $Prefix \"llmp.ps1\"),',\n ' (Join-Path $Prefix \"llmp\")',\n ' )',\n ' foreach ($candidate in $candidates) {',\n ' if (Test-Path $candidate) { return $candidate }',\n ' }',\n ' return (Join-Path $Prefix \"llmproxy.cmd\")',\n '}',\n '',\n '# Find the newly installed binary',\n '$npmPrefix = ((npm prefix -g 2>$null) | Out-String).Trim()',\n '$newBin = Resolve-LlmproxyGlobalBin $npmPrefix',\n '$installDir = Join-Path ((npm root -g).Trim()) \"llmproxy\"',\n '$packageCli = Join-Path $installDir \"bin/llmproxy.js\"',\n '',\n `$localeArg = ${locale === \"en\" ? '\"en\"' : '\"it\"'}`,\n '',\n 'function Invoke-Rollback([string]$Reason) {',\n ' if ($rollbackAvailable -and (Test-Path $rollbackDir)) {',\n ' if ($installDir -and (Test-Path $installDir)) { Remove-Item -Recurse -Force $installDir -ErrorAction SilentlyContinue }',\n ' $parentDir = Split-Path $installDir -Parent',\n ' if ($parentDir) { New-Item -ItemType Directory -Path $parentDir -Force | Out-Null }',\n ' Copy-Item -Recurse -Force $rollbackDir $installDir',\n ' $restoredCli = Join-Path $installDir \"bin/llmproxy.js\"',\n ' if (Test-Path $restoredCli) {',\n ' node $restoredCli service:restart 2>$null | Out-Null',\n ' $restoredVersion = ((node $restoredCli version 2>$null) | Out-String).Trim()',\n ' Write-Output \"__LLMPROXY_ROLLBACK__=1\"',\n ' Write-Output \"__LLMPROXY_ROLLBACK_VERSION__=$restoredVersion\"',\n ' Write-Output \"__LLMPROXY_ROLLBACK_REASON__=$Reason\"',\n ' exit 0',\n ' }',\n ' }',\n ' throw \"Update verification failed: $Reason\"',\n '}',\n '',\n '# Migrate legacy managed config variables to the current schema',\n 'Invoke-QuietNative \"$newBin\" @(\"config:migrate\")',\n '',\n '# Restart service via the new binary',\n 'Invoke-QuietNative \"$newBin\" @(\"service:restart\")',\n '$resolvedVersion = ((& \"$newBin\" version 2>$null) | Out-String).Trim()',\n 'if ($resolvedVersion -ne $targetVersion) { Invoke-Rollback \"installed binary verification failed for $targetVersion\" }',\n '& \"$newBin\" config:list 2>$null | Out-Null',\n 'if ($LASTEXITCODE -ne 0) { Invoke-Rollback \"config:list failed after installing $targetVersion\" }',\n '& \"$newBin\" status 2>$null | Out-Null',\n 'if ($LASTEXITCODE -ne 0) { Invoke-Rollback \"post-update smoke test failed for $targetVersion\" }',\n '',\n '# Output results',\n '$versionOutput = $resolvedVersion',\n '$releaseNotesOutput = & \"$newBin\" release-notes --version $targetVersion --locale ' + (locale === \"en\" ? '\"en\"' : '\"it\"') + ' --commit-message-base64 $commitMessageBase64 2>&1',\n 'Write-Output \"__LLMPROXY_VERSION__=$versionOutput\"',\n 'Write-Output \"__LLMPROXY_RELEASE_NOTES_START__\"',\n 'Write-Output \"$releaseNotesOutput\"',\n 'Write-Output \"__LLMPROXY_RELEASE_NOTES_END__\"',\n ].join(\"\\n\");\n return commandRunner(\"powershell.exe\", [\"-NoProfile\", \"-Command\", psScript], { encoding: \"utf8\" });\n}\n\nfunction runSelfUninstallWindows(commandRunner = spawnSync) {\n const psScript = [\n '$ErrorActionPreference = \"Stop\"',\n 'npm uninstall -g llmproxy 2>$null | Out-Null',\n 'pnpm remove -g llmproxy 2>$null | Out-Null',\n '$npmPrefix = ((npm prefix -g 2>$null) | Out-String).Trim()',\n 'if ($npmPrefix) {',\n ' @(',\n ' \"llmproxy\", \"llmproxy.cmd\", \"llmproxy.ps1\",',\n ' \"llmp\", \"llmp.cmd\", \"llmp.ps1\"',\n ' ) | ForEach-Object {',\n ' $candidate = Join-Path $npmPrefix $_',\n ' Remove-Item -Force $candidate -ErrorAction SilentlyContinue',\n ' }',\n '}',\n '$pnpmRoot = pnpm root -g 2>$null',\n 'if ($pnpmRoot) {',\n ' $pnpmHome = Split-Path (Split-Path $pnpmRoot) -Parent',\n ' @(',\n ' \"llmproxy\", \"llmproxy.cmd\", \"llmproxy.ps1\",',\n ' \"llmp\", \"llmp.cmd\", \"llmp.ps1\"',\n ' ) | ForEach-Object {',\n ' Remove-Item -Force (Join-Path \"$pnpmHome/bin\" $_) -ErrorAction SilentlyContinue',\n ' }',\n '}',\n ].join(\"\\n\");\n return commandRunner(\"powershell.exe\", [\"-NoProfile\", \"-Command\", psScript], { encoding: \"utf8\" });\n}\n\nfunction buildWindowsPersistentInstallScript(options = {}) {\n const packageRoot = path.resolve(String(options.packageRoot || path.join(__dirname, \"..\")));\n const locale = normalizeLocale(options.locale);\n const lines = locale === \"en\"\n ? [\n \"$ErrorActionPreference = 'Stop'\",\n \"$tmpdir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())\",\n \"New-Item -ItemType Directory -Path $tmpdir -Force | Out-Null\",\n `$packageRoot = ${JSON.stringify(packageRoot)}`,\n \"& llmproxy service:stop 2>$null | Out-Null\",\n \"npm pack $packageRoot --pack-destination $tmpdir 2>&1 | Out-Null\",\n \"$packageFile = Get-ChildItem (Join-Path $tmpdir '*.tgz') | Select-Object -First 1 -ExpandProperty FullName\",\n \"if (-not $packageFile) { throw 'Unable to prepare llmproxy package archive for persistent install.' }\",\n \"# Remove previous global installs\",\n \"npm uninstall -g llmproxy 2>$null | Out-Null\",\n \"pnpm remove -g llmproxy 2>$null | Out-Null\",\n \"# Install globally\",\n \"npm install -g $packageFile 2>&1 | Out-Null\",\n \"# Remove pnpm global wrapper if it exists\",\n \"$pnpmRoot = pnpm root -g 2>$null\",\n \"if ($pnpmRoot) {\",\n \" $pnpmHome = Split-Path (Split-Path $pnpmRoot) -Parent\",\n \" Remove-Item -Force \\\"$pnpmHome/bin/llmproxy\\\" -ErrorAction SilentlyContinue\",\n \"}\",\n \"function Resolve-LlmproxyGlobalBin([string]$Prefix) {\",\n \" $candidates = @(\",\n \" (Join-Path $Prefix \\\"llmproxy.cmd\\\"),\",\n \" (Join-Path $Prefix \\\"llmproxy.ps1\\\"),\",\n \" (Join-Path $Prefix \\\"llmproxy\\\"),\",\n \" (Join-Path $Prefix \\\"llmp.cmd\\\"),\",\n \" (Join-Path $Prefix \\\"llmp.ps1\\\"),\",\n \" (Join-Path $Prefix \\\"llmp\\\")\",\n \" )\",\n \" foreach ($candidate in $candidates) {\",\n \" if (Test-Path $candidate) { return $candidate }\",\n \" }\",\n \" return (Join-Path $Prefix \\\"llmproxy.cmd\\\")\",\n \"}\",\n \"# Remove other llmproxy binaries from PATH (keep the npm one)\",\n \"$npmPrefix = ((npm prefix -g 2>$null) | Out-String).Trim()\",\n \"$globalBin = Resolve-LlmproxyGlobalBin $npmPrefix\",\n \"$existing = Get-Command llmproxy -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source\",\n \"foreach ($bin in $existing) {\",\n \" if ($bin -ne $globalBin -and (Test-Path $bin)) { Remove-Item -Force $bin -ErrorAction SilentlyContinue }\",\n \"}\",\n \"# Start persistent service via the newly installed global binary\",\n \"$env:LLMPROXY_MODE = 'standalone'\",\n \"$env:LLMPROXY_SERVICE_RUNTIME = 'native'\",\n \"& \\\"$globalBin\\\" service:start 2>&1\",\n \"Write-Output \\\"__LLMPROXY_GLOBAL_BIN__=$globalBin\\\"\",\n \"Write-Output \\\"__LLMPROXY_BIN_SCOPE__=user\\\"\",\n \"Remove-Item -Recurse -Force $tmpdir -ErrorAction SilentlyContinue\",\n ]\n : [\n \"$ErrorActionPreference = 'Stop'\",\n \"$tmpdir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())\",\n \"New-Item -ItemType Directory -Path $tmpdir -Force | Out-Null\",\n `$packageRoot = ${JSON.stringify(packageRoot)}`,\n \"& llmproxy service:stop 2>$null | Out-Null\",\n \"npm pack $packageRoot --pack-destination $tmpdir 2>&1 | Out-Null\",\n \"$packageFile = Get-ChildItem (Join-Path $tmpdir '*.tgz') | Select-Object -First 1 -ExpandProperty FullName\",\n \"if (-not $packageFile) { throw 'Impossibile preparare l\\\\'archivio del package llmproxy per l\\\\'installazione persistente.' }\",\n \"# Rimuovi installazioni globali precedenti\",\n \"npm uninstall -g llmproxy 2>$null | Out-Null\",\n \"pnpm remove -g llmproxy 2>$null | Out-Null\",\n \"# Installa globalmente\",\n \"npm install -g $packageFile 2>&1 | Out-Null\",\n \"# Rimuovi wrapper pnpm globale se esiste\",\n \"$pnpmRoot = pnpm root -g 2>$null\",\n \"if ($pnpmRoot) {\",\n \" $pnpmHome = Split-Path (Split-Path $pnpmRoot) -Parent\",\n \" Remove-Item -Force \\\"$pnpmHome/bin/llmproxy\\\" -ErrorAction SilentlyContinue\",\n \"}\",\n \"function Resolve-LlmproxyGlobalBin([string]$Prefix) {\",\n \" $candidates = @(\",\n \" (Join-Path $Prefix \\\"llmproxy.cmd\\\"),\",\n \" (Join-Path $Prefix \\\"llmproxy.ps1\\\"),\",\n \" (Join-Path $Prefix \\\"llmproxy\\\"),\",\n \" (Join-Path $Prefix \\\"llmp.cmd\\\"),\",\n \" (Join-Path $Prefix \\\"llmp.ps1\\\"),\",\n \" (Join-Path $Prefix \\\"llmp\\\")\",\n \" )\",\n \" foreach ($candidate in $candidates) {\",\n \" if (Test-Path $candidate) { return $candidate }\",\n \" }\",\n \" return (Join-Path $Prefix \\\"llmproxy.cmd\\\")\",\n \"}\",\n \"# Rimuovi altri binari llmproxy dal PATH (mantieni quello npm)\",\n \"$npmPrefix = ((npm prefix -g 2>$null) | Out-String).Trim()\",\n \"$globalBin = Resolve-LlmproxyGlobalBin $npmPrefix\",\n \"$existing = Get-Command llmproxy -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source\",\n \"foreach ($bin in $existing) {\",\n \" if ($bin -ne $globalBin -and (Test-Path $bin)) { Remove-Item -Force $bin -ErrorAction SilentlyContinue }\",\n \"}\",\n \"# Avvia servizio persistente tramite il binario globale appena installato\",\n \"$env:LLMPROXY_MODE = 'standalone'\",\n \"$env:LLMPROXY_SERVICE_RUNTIME = 'native'\",\n \"& \\\"$globalBin\\\" service:start 2>&1\",\n \"Write-Output \\\"__LLMPROXY_GLOBAL_BIN__=$globalBin\\\"\",\n \"Write-Output \\\"__LLMPROXY_BIN_SCOPE__=user\\\"\",\n \"Remove-Item -Recurse -Force $tmpdir -ErrorAction SilentlyContinue\",\n ];\n\n return lines.join(\"\\n\");\n}\n\nfunction buildPersistentInstallScript(options = {}) {\n const platform = String(options.platform || process.platform);\n const packageRoot = path.resolve(String(options.packageRoot || path.join(__dirname, \"..\")));\n const locale = options.locale === \"en\" ? \"en\" : \"it\";\n\n if (platform === \"win32\") {\n return buildWindowsPersistentInstallScript(options);\n }\n\n if (platform !== \"darwin\" && platform !== \"linux\") {\n if (locale === \"en\") {\n throw new Error(`Unsupported platform for persistent installation: ${platform}`);\n }\n throw new Error(`Piattaforma non supportata per installazione persistente: ${platform}`);\n }\n\n return [\n \"set -e\",\n `platform=${shellQuote(platform)}`,\n 'case \"$platform\" in',\n ' darwin|linux) ;;',\n ' *) echo \"Unsupported platform: $platform\" >&2; exit 1 ;;',\n 'esac',\n 'append_path_export_once() {',\n ' profile_file=\"$1\"',\n ' bin_dir=\"$2\"',\n ' [ -n \"$profile_file\" ] || return 0',\n ' [ -n \"$bin_dir\" ] || return 0',\n ' line=\"export PATH=\\\\\"$bin_dir:\\\\$PATH\\\\\"\"',\n ' if [ ! -f \"$profile_file\" ]; then',\n ' : > \"$profile_file\"',\n ' fi',\n ' grep -F \"$line\" \"$profile_file\" >/dev/null 2>&1 && return 0',\n ' printf \"\\\\n%s\\\\n\" \"$line\" >> \"$profile_file\"',\n '}',\n 'persist_user_npm_bin_path() {',\n ' prefix=\"$1\"',\n ' case \"$prefix\" in',\n ' \"$HOME\"/*) ;;',\n ' *) return 0 ;;',\n ' esac',\n ' bin_dir=\"$prefix/bin\"',\n ' [ -d \"$bin_dir\" ] || return 0',\n ' append_path_export_once \"$HOME/.profile\" \"$bin_dir\"',\n ' append_path_export_once \"$HOME/.bash_profile\" \"$bin_dir\"',\n ' append_path_export_once \"$HOME/.zprofile\" \"$bin_dir\"',\n ' PATH=\"$bin_dir:$PATH\"',\n ' export PATH',\n ' hash -r 2>/dev/null || true',\n '}',\n 'cleanup_global_service_port() {',\n ' service_port=\"${1:-7045}\"',\n ' [ -n \"$service_port\" ] || return 0',\n ' if command -v docker >/dev/null 2>&1; then',\n ' docker_ids=$(docker ps --format \\'{{.ID}} {{.Ports}}\\' 2>/dev/null | awk -v port=\"$service_port\" \\'index($0, \":\" port \"->\") > 0 { print $1 }\\')',\n ' if [ -n \"$docker_ids\" ]; then',\n ' for docker_id in $docker_ids; do',\n ' docker stop \"$docker_id\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo docker stop \"$docker_id\" >/dev/null 2>&1 || true; else true; fi; }',\n ' docker rm \"$docker_id\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo docker rm \"$docker_id\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' fi',\n ' fi',\n ' if command -v lsof >/dev/null 2>&1; then',\n ' listener_pids=$(lsof -tiTCP:\"$service_port\" -sTCP:LISTEN 2>/dev/null || true)',\n ' if [ -n \"$listener_pids\" ]; then',\n ' for listener_pid in $listener_pids; do',\n ' kill \"$listener_pid\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo kill \"$listener_pid\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' sleep 1',\n ' listener_pids=$(lsof -tiTCP:\"$service_port\" -sTCP:LISTEN 2>/dev/null || true)',\n ' if [ -n \"$listener_pids\" ]; then',\n ' for listener_pid in $listener_pids; do',\n ' kill -9 \"$listener_pid\" >/dev/null 2>&1 || { if command -v sudo >/dev/null 2>&1; then sudo kill -9 \"$listener_pid\" >/dev/null 2>&1 || true; else true; fi; }',\n ' done',\n ' fi',\n ' fi',\n ' fi',\n '}',\n 'ensure_runtime_home_permissions() {',\n ' runtime_home=\"$1\"',\n ' runtime_owner=\"$2\"',\n ' [ -n \"$runtime_home\" ] || return 0',\n ' mkdir -p \"$runtime_home/logs\" >/dev/null 2>&1 || {',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo mkdir -p \"$runtime_home/logs\" >/dev/null 2>&1 || true',\n ' fi',\n ' }',\n ' touch \"$runtime_home/logs/service.out.log\" \"$runtime_home/logs/service.err.log\" \"$runtime_home/logs/metering.jsonl\" >/dev/null 2>&1 || {',\n ' if command -v sudo >/dev/null 2>&1; then',\n ' sudo touch \"$runtime_home/logs/service.out.log\" \"$runtime_home/logs/service.err.log\" \"$runtime_home/logs/metering.jsonl\" >/dev/null 2>&1 || true',\n ' fi',\n ' }',\n ' if [ -n \"$runtime_owner\" ] && command -v sudo >/dev/null 2>&1; then',\n ' sudo chown -R \"$runtime_owner\":\"$runtime_owner\" \"$runtime_home\" >/dev/null 2>&1 || true',\n ' fi',\n ' chmod -R u+rwX \"$runtime_home\" >/dev/null 2>&1 || true',\n '}',\n \"\",\n \"# Block reinstall if llmproxy is already installed system-wide by another user\",\n 'existing_global_bin=\"\"',\n 'for candidate in $( (which -a llmproxy 2>/dev/null; which -a llmp 2>/dev/null) | awk \\'!seen[$0]++\\'); do',\n ' case \"$candidate\" in',\n ' \"$HOME\"/*) ;;',\n ' *) existing_global_bin=\"$candidate\"; break ;;',\n ' esac',\n 'done',\n 'if [ -n \"$existing_global_bin\" ]; then',\n ' bin_owner=$(stat -c \"%U\" \"$existing_global_bin\" 2>/dev/null || stat -f \"%Su\" \"$existing_global_bin\" 2>/dev/null || true)',\n ' if [ -n \"$bin_owner\" ] && [ \"$bin_owner\" != \"$(whoami)\" ] && [ \"$bin_owner\" != \"root\" ]; then',\n ` echo \"llmproxy e' gia' installato globalmente da un altro utente ($bin_owner).\" >&2`,\n ' echo \"Usa llmproxy update per aggiornare, oppure contatta l\\'amministratore di sistema.\" >&2',\n ' echo \"Per forzare la reinstallazione usa --force (sovrascrive l\\'installazione esistente).\" >&2',\n ' exit 1',\n ' fi',\n 'fi',\n \"\",\n \"existing_bins=$( (which -a llmproxy 2>/dev/null; which -a llmp 2>/dev/null) | awk '!seen[$0]++')\",\n \"used_sudo=0\",\n 'npm_prefix_global=$(npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'case \"$npm_prefix_global\" in',\n ' \"$HOME\"/*)',\n ' # User-local npm prefix: prefer the existing writable prefix first',\n ` if ! npm install -g ${shellQuote(packageRoot)} 2>/dev/null; then`,\n ' if command -v sudo >/dev/null 2>&1; then',\n ` if sudo npm install -g ${shellQuote(packageRoot)} 2>/dev/null; then`,\n ' used_sudo=1',\n ' echo \"System-wide install via sudo (user-local prefix install failed).\"',\n ' else',\n ' echo \"Error: npm install failed.\" >&2',\n ' exit 1',\n ' fi',\n ' else',\n ' echo \"Error: npm install failed. Install sudo or fix npm permissions.\" >&2',\n ' exit 1',\n ' fi',\n ' fi',\n ' ;;',\n ' *)',\n ' # System-level npm prefix: normal install, fall back to sudo if permissions fail',\n ` if ! npm install -g ${shellQuote(packageRoot)} 2>/dev/null; then`,\n ' echo \"Standard npm install failed, trying with sudo...\"',\n ' if command -v sudo >/dev/null 2>&1; then',\n ` if sudo npm install -g ${shellQuote(packageRoot)} 2>/dev/null; then`,\n ' used_sudo=1',\n ' else',\n ' echo \"Error: Cannot install llmproxy globally. Run with sudo or fix npm permissions.\" >&2',\n ' exit 1',\n ' fi',\n ' else',\n ' echo \"Error: npm install failed and sudo is not available. Fix npm permissions or run as root.\" >&2',\n ' exit 1',\n ' fi',\n ' fi',\n ' ;;',\n 'esac',\n 'pnpm remove -g llmproxy >/dev/null 2>&1 || true',\n 'pnpm_root=$(pnpm root -g 2>/dev/null || true)',\n 'if [ -n \"$pnpm_root\" ]; then',\n ' pnpm_home=$(dirname \"$(dirname \"$pnpm_root\")\")',\n ' rm -f \"$pnpm_home/bin/llmproxy\"',\n ' rm -f \"$pnpm_home/bin/llmp\"',\n 'fi',\n \"# Re-read npm prefix: use sudo when we installed system-wide\",\n 'if [ \"$used_sudo\" -eq 1 ]; then',\n ' npm_prefix=$(sudo npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'else',\n ' npm_prefix=$(npm prefix -g 2>/dev/null || echo \"/usr/local\")',\n 'fi',\n 'persist_user_npm_bin_path \"$npm_prefix\"',\n \"\",\n \"# System-wide install: make installed package world-readable (keep root ownership)\",\n 'if [ \"$used_sudo\" -eq 1 ]; then',\n ' install_dir=\"$npm_prefix/lib/node_modules/llmproxy\"',\n ' if [ -d \"$install_dir\" ]; then',\n ' sudo chmod -R a+rX \"$install_dir\" 2>/dev/null || echo \"Warning: could not make $install_dir world-readable. Non-root users may not be able to use llmproxy.\" >&2',\n ' fi',\n 'fi',\n \"\",\n 'global_bin=\"$npm_prefix/bin/llmproxy\"',\n 'global_short_bin=\"$npm_prefix/bin/llmp\"',\n '[ -x \"$global_bin\" ]',\n 'linger_user=${SUDO_USER:-$USER}',\n 'for installed_bin in $existing_bins; do',\n ' if [ -n \"$installed_bin\" ] && [ \"$installed_bin\" != \"$global_bin\" ] && [ \"$installed_bin\" != \"$global_short_bin\" ]; then',\n ' rm -f \"$installed_bin\"',\n ' fi',\n 'done',\n 'if [ \"$platform\" = \"linux\" ] && command -v sudo >/dev/null 2>&1; then',\n ' sudo -n loginctl enable-linger \"$linger_user\" >/dev/null 2>&1 || true',\n 'fi',\n '# On Linux, wait for systemd --user bus to become available after enable-linger',\n 'if [ \"$platform\" = \"linux\" ] && [ -z \"${XDG_RUNTIME_DIR:-}\" ]; then',\n ' uid=$(id -u)',\n ' export XDG_RUNTIME_DIR=\"/run/user/$uid\"',\n 'fi',\n 'if [ \"$platform\" = \"linux\" ] && [ -d \"$XDG_RUNTIME_DIR\" ]; then',\n ' bus_socket=\"$XDG_RUNTIME_DIR/bus\"',\n ' if [ ! -S \"$bus_socket\" ] && command -v systemctl >/dev/null 2>&1; then',\n ' systemctl --user start dbus 2>/dev/null || true',\n ' # Wait up to 5s for the user bus socket to appear',\n ' waited=0',\n ' while [ ! -S \"$bus_socket\" ] && [ \"$waited\" -lt 5 ]; do',\n ' sleep 1',\n ' waited=$((waited + 1))',\n ' done',\n ' fi',\n 'fi',\n 'if [ -z \"$LLMPROXY_HOME\" ]; then',\n ' if [ \"$platform\" = \"darwin\" ]; then',\n ' export LLMPROXY_HOME=\"$HOME/Library/Application Support/llmProxy\"',\n ' else',\n ' export LLMPROXY_HOME=\"$HOME/.local/share/llmProxy\"',\n ' fi',\n 'fi',\n 'runtime_owner=\"\"',\n 'if [ \"$used_sudo\" -eq 1 ] && [ -n \"${SUDO_USER:-}\" ]; then',\n ' runtime_owner=\"$SUDO_USER\"',\n 'else',\n ' runtime_owner=\"$(whoami)\"',\n 'fi',\n 'ensure_runtime_home_permissions \"$LLMPROXY_HOME\" \"$runtime_owner\"',\n 'cleanup_global_service_port \"${PORT:-7045}\"',\n '# When npm install used sudo, run service:start as the original user (systemd --user needs the real user session bus)',\n 'if [ \"$used_sudo\" -eq 1 ] && [ \"$platform\" = \"linux\" ] && [ -n \"${SUDO_USER:-}\" ] && command -v sudo >/dev/null 2>&1; then',\n ' 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 'else',\n ' LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"$global_bin\" service:start',\n 'fi',\n 'printf \"__LLMPROXY_GLOBAL_BIN__=%s\\\\n\" \"$global_bin\"',\n 'case \"$global_bin\" in',\n ' \"$HOME\"/*)',\n ' printf \"__LLMPROXY_BIN_SCOPE__=user\\\\n\"',\n ` printf \"__LLMPROXY_BIN_HINT__=%s\\\\n\" \"Il binario globale e' sotto \\\\$HOME ($global_bin). Altri utenti non vedranno llmproxy. Per installarlo a livello server, imposta il prefix npm su un path di sistema (es. /usr/local) e riesegui con sudo.\" ;;`,\n ' *) printf \"__LLMPROXY_BIN_SCOPE__=server\\\\n\" ;;',\n 'esac',\n \"\",\n \"# Suggest cleanup of clone directory\",\n `clone_dir=${shellQuote(packageRoot)}`,\n 'case \"$clone_dir\" in',\n ' /tmp/*|\"$HOME\"/llmProxy|\"$HOME\"/llmproxy|\"$HOME\"/Modules-Platform/*)',\n ' printf \"__LLMPROXY_CLONE_DIR__=%s\\\\n\" \"$clone_dir\"',\n ' echo \"\"',\n ' echo \"Il codice e\\' stato installato globalmente. La directory di clone non serve piu\\'.\"',\n ` echo \"Puoi rimuoverla con: rm -rf $clone_dir\"`,\n ' echo \"\"',\n ' ;;',\n 'esac',\n ].join(\"\\n\");\n}\n\nfunction resolveInstallLocale(command) {\n if (command === \"install\" || command === \"install:persistent-en\") return \"en\";\n return \"it\";\n}\n\nfunction isPersistentInstallCommand(command) {\n return command === \"install\"\n || command === \"install:persistent\"\n || command === \"install:persistent-it\"\n || command === \"install:persistent-en\";\n}\n\nfunction runPersistentInstall(commandRunner = spawnSync, options = {}) {\n const platform = String(options.platform || process.platform);\n if (platform === \"win32\") {\n const psScript = buildPersistentInstallScript(options);\n return commandRunner(\"powershell.exe\", [\"-NoProfile\", \"-Command\", psScript], { encoding: \"utf8\" });\n }\n return commandRunner(\"sh\", [\"-c\", buildPersistentInstallScript(options)], { encoding: \"utf8\" });\n}\n\nfunction getProxyBaseUrl(envOrOptions = process.env) {\n const options = envOrOptions && typeof envOrOptions === \"object\"\n && (Object.prototype.hasOwnProperty.call(envOrOptions, \"env\")\n || Object.prototype.hasOwnProperty.call(envOrOptions, \"dataRoot\")\n || Object.prototype.hasOwnProperty.call(envOrOptions, \"host\")\n || Object.prototype.hasOwnProperty.call(envOrOptions, \"port\"))\n ? envOrOptions\n : { env: envOrOptions };\n const { host, port } = resolveProxyHostPort(options);\n return `http://${host}:${port}`;\n}\n\nfunction resolveCliServiceManagerOptions({ env, paths, targetPlatform }) {\n const dockerComposeFile = path.join(paths.packageRoot, \"docker-compose.production.yml\");\n const proxyBinding = resolveProxyHostPort({ env, dataRoot: paths.dataRoot });\n\n return {\n platform: targetPlatform,\n packageRoot: paths.packageRoot,\n entryFile: resolveServiceEntryFile({ env, packageRoot: paths.packageRoot, targetPlatform }),\n serviceFile: targetPlatform === \"darwin\" ? paths.launchAgentFile : targetPlatform === \"win32\" ? paths.serviceRunnerFile : paths.systemdUnitFile,\n wrapperPath: targetPlatform === \"win32\" ? paths.serviceRunnerFile : undefined,\n stdoutPath: paths.stdoutLogFile,\n stderrPath: paths.stderrLogFile,\n environment: resolveServiceEnvironment({\n env: { ...env, HOST: proxyBinding.host, PORT: proxyBinding.port },\n paths,\n dockerComposeFile,\n }),\n };\n}\n\nfunction resolveServiceEnvironment({ env = process.env, paths, dockerComposeFile }) {\n const binding = resolveProxyHostPort({ env, dataRoot: paths.dataRoot });\n return {\n PORT: String(binding.port),\n HOST: String(binding.host),\n LLMPROXY_GLOBAL_SERVICE: \"1\",\n NODE_ENV: String(env.NODE_ENV || \"production\"),\n LLMPROXY_ENV: String(env.LLMPROXY_ENV || env.NODE_ENV || \"production\"),\n LLMPROXY_RUNTIME_PROFILE: String(env.LLMPROXY_RUNTIME_PROFILE || env.LLMPROXY_ENV || env.NODE_ENV || \"production\"),\n LLMPROXY_HOME: paths.dataRoot,\n LLMPROXY_MODE: String(env.LLMPROXY_MODE || \"standalone\"),\n LLMPROXY_SERVICE_RUNTIME: String(env.LLMPROXY_SERVICE_RUNTIME || \"native\"),\n LLMPROXY_MONGODB_CONNECTION_STRING: String(env.LLMPROXY_MONGODB_CONNECTION_STRING || \"\"),\n DBLAYER_URL: resolveDbLayerUrlForEnv(env),\n EVENTBUS_URL: resolveEventBusUrlForEnv(env),\n LLMPROXY_LOG_RETENTION_DAYS: String(env.LLMPROXY_LOG_RETENTION_DAYS || \"30\"),\n LLMPROXY_HOST_PROJECTS_ROOT: String(\n env.LLMPROXY_HOST_PROJECTS_ROOT || getDockerHostProjectsRoot({ env }),\n ),\n LLMPROXY_PROXY_REGISTRY: paths.proxyRegistryFile,\n LLMPROXY_DOCKER_COMPOSE_FILE: dockerComposeFile,\n LLMPROXY_DOCKER_SERVICE: String(env.LLMPROXY_DOCKER_SERVICE || \"llmproxy\"),\n LLMPROXY_DOCKER_POLL_MS: String(env.LLMPROXY_DOCKER_POLL_MS || \"30000\"),\n PATH: String(env.PATH || \"/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin\"),\n };\n}\n\nasync function checkServiceHealth(fetchFn, baseUrl) {\n const response = await fetchFn(`${String(baseUrl).replace(/\\/$/, \"\")}/health`, {\n signal: AbortSignal.timeout(3000),\n });\n return response.ok;\n}\n\nasync function validatePlatformModeSwitch({ fetchFn, env, serviceConfigFile, nextValue }) {\n const normalized = String(nextValue || \"\").trim().toLowerCase();\n if (normalized !== \"platform\") return;\n\n const currentServiceConfig = readServiceConfig(serviceConfigFile);\n const mergedEnv = {\n ...env,\n ...(currentServiceConfig?.env || {}),\n LLMPROXY_MODE: \"platform\",\n };\n const dbLayerUrl = resolveDbLayerUrlForEnv(mergedEnv);\n const eventBusUrl = resolveEventBusUrlForEnv(mergedEnv);\n\n const [dbLayerOk, eventBusOk] = await Promise.all([\n checkServiceHealth(fetchFn, dbLayerUrl).catch(() => false),\n checkServiceHealth(fetchFn, eventBusUrl).catch(() => false),\n ]);\n\n if (dbLayerOk && eventBusOk) return;\n\n const missing = [];\n if (!dbLayerOk) missing.push(`db-layer (${dbLayerUrl})`);\n if (!eventBusOk) missing.push(`event-bus (${eventBusUrl})`);\n throw new Error(`Non e' possibile passare a LLMPROXY_MODE=platform: dipendenze non raggiungibili: ${missing.join(\", \")}`);\n}\n\nfunction resolveServiceEntryFile({ env = process.env, packageRoot, targetPlatform = process.platform }) {\n const forcedRuntime = String(env.LLMPROXY_SERVICE_RUNTIME || \"\").trim().toLowerCase();\n if (forcedRuntime === \"docker\") {\n return path.join(packageRoot, \"lib\", \"service\", \"docker-launchd-entry.js\");\n }\n // Native (default): node, native, o non impostato → server.js diretto\n return path.join(packageRoot, \"server.js\");\n}\n\nfunction writeClaudeSettings(options = {}) {\n const cwd = path.resolve(String(options.cwd || process.cwd()));\n const env = options.env || process.env;\n const stdout = options.stdout || process.stdout;\n const selectedModel = String(options.model || \"\").trim();\n const settingsDir = path.join(cwd, \".claude\");\n const settingsFile = path.join(settingsDir, \"settings.json\");\n let existingConfig = {};\n\n if (fs.existsSync(settingsFile)) {\n existingConfig = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n }\n\n const proxyEnvSource = env.LLMPROXY_HOME && options.port == null\n ? { ...env, PORT: \"\" }\n : env;\n const proxyEnv = {\n ANTHROPIC_BASE_URL: getProxyBaseUrl({\n env: proxyEnvSource,\n dataRoot: env.LLMPROXY_HOME || options.dataRoot,\n host: options.host,\n port: options.port,\n }),\n API_TIMEOUT_MS: \"3000000\",\n CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: \"1\",\n LLMPROXY_LLM_STATS_API_KEY: \"\",\n LLMPROXY_SENDGRID_API_KEY: \"\",\n LLMPROXY_SENDGRID_FROM_EMAIL: \"\",\n LLMPROXY_SENDGRID_TO_EMAIL: \"\",\n LLMPROXY_SENDGRID_TO_MESSAGE_TYPE: \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\",\n LLMPROXY_METERING_INLINE: \"0\",\n LLMPROXY_INFERENCE_INFO_INLINE: \"1\",\n LLMPROXY_PROVIDER_CREDIT_INLINE: \"1\",\n LLMPROXY_SHORT_ANSWER: \"0\",\n };\n if (options.includeDefaultModel !== false) {\n proxyEnv.ANTHROPIC_DEFAULT_MODEL = selectedModel;\n }\n const uiModelLabel = String(options.uiModelLabel || \"llmProxy\").trim() || \"llmProxy\";\n const existingEnv = existingConfig.env && typeof existingConfig.env === \"object\" ? existingConfig.env : {};\n if (options.includeDefaultModel === false) {\n delete existingEnv.ANTHROPIC_DEFAULT_MODEL;\n }\n delete existingEnv.ANTHROPIC_AUTH_TOKEN;\n const nextConfig = {\n ...existingConfig,\n model: uiModelLabel,\n env: {\n ...existingEnv,\n ...proxyEnv,\n },\n };\n\n fs.mkdirSync(settingsDir, { recursive: true });\n fs.writeFileSync(settingsFile, `${JSON.stringify(nextConfig, null, 2)}\\n`, \"utf8\");\n stdout.write(`Configurazione Claude scritta in ${settingsFile}\\n`);\n stdout.write(`ANTHROPIC_BASE_URL: ${proxyEnv.ANTHROPIC_BASE_URL}\\n`);\n stdout.write(`Default model: ${selectedModel}\\n`);\n syncClaudeHomeSettings({\n env,\n proxyEnv: {\n ...proxyEnv,\n ANTHROPIC_AUTH_TOKEN: \"proxy-local\",\n },\n stdout,\n });\n return 0;\n}\n\nfunction syncClaudeHomeSettings(options = {}) {\n const env = options.env || process.env;\n const stdout = options.stdout || process.stdout;\n const proxyEnv = options.proxyEnv && typeof options.proxyEnv === \"object\" ? options.proxyEnv : {};\n const homeDir = path.resolve(String(env.HOME || os.homedir()));\n const settingsDir = path.join(homeDir, \".claude\");\n const settingsFile = path.join(settingsDir, \"settings.json\");\n let existingConfig = {};\n\n if (fs.existsSync(settingsFile)) {\n existingConfig = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n }\n\n const existingEnv = existingConfig.env && typeof existingConfig.env === \"object\" ? existingConfig.env : {};\n const nextConfig = {\n ...existingConfig,\n env: {\n ...existingEnv,\n ANTHROPIC_BASE_URL: proxyEnv.ANTHROPIC_BASE_URL || existingEnv.ANTHROPIC_BASE_URL,\n API_TIMEOUT_MS: proxyEnv.API_TIMEOUT_MS || existingEnv.API_TIMEOUT_MS,\n CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: proxyEnv.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS || existingEnv.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS,\n ANTHROPIC_AUTH_TOKEN: proxyEnv.ANTHROPIC_AUTH_TOKEN,\n },\n };\n\n delete nextConfig.env.ANTHROPIC_DEFAULT_MODEL;\n\n fs.mkdirSync(settingsDir, { recursive: true });\n fs.writeFileSync(settingsFile, `${JSON.stringify(nextConfig, null, 2)}\\n`, \"utf8\");\n stdout.write(\"Supporto globale Claude sincronizzato automaticamente (placeholder auth locale).\\n\");\n}\n\nfunction writePiSettings(options = {}) {\n const cwd = path.resolve(String(options.cwd || process.cwd()));\n const env = options.env || process.env;\n const stdout = options.stdout || process.stdout;\n const piDir = path.join(cwd, \".pi\");\n const modelsFile = path.join(piDir, \"models.json\");\n const settingsFile = path.join(piDir, \"settings.json\");\n\n const proxyEnvSource = env.LLMPROXY_HOME && options.port == null\n ? { ...env, PORT: \"\" }\n : env;\n const baseUrl = getProxyBaseUrl({\n env: proxyEnvSource,\n dataRoot: env.LLMPROXY_HOME || options.dataRoot,\n host: options.host,\n port: options.port,\n });\n\n const models = {\n providers: {\n llmproxy: {\n api: \"anthropic-messages\",\n baseUrl,\n apiKey: \"proxy-local\",\n models: [{ id: \"llmproxy\", name: \"llmProxy\", contextWindow: 1000000 }],\n },\n },\n };\n\n const settings = {\n defaultProvider: \"llmproxy\",\n defaultModel: \"llmproxy\",\n };\n\n fs.mkdirSync(piDir, { recursive: true });\n fs.writeFileSync(modelsFile, `${JSON.stringify(models, null, 2)}\\n`, \"utf8\");\n fs.writeFileSync(settingsFile, `${JSON.stringify(settings, null, 2)}\\n`, \"utf8\");\n stdout.write(`Configurazione PI Agent scritta in ${piDir}/\\n`);\n stdout.write(` ${modelsFile}\\n`);\n stdout.write(` ${settingsFile}\\n`);\n stdout.write(`baseUrl: ${baseUrl}\\n`);\n return 0;\n}\n\nfunction resolveVscodeChatModelsFile(env = process.env) {\n const platform = String(env.LLMPROXY_PLATFORM_OVERRIDE || process.platform);\n const homeDir = path.resolve(String(env.HOME || os.homedir()));\n if (platform === \"win32\") {\n const appData = String(env.APPDATA || path.join(homeDir, \"AppData\", \"Roaming\"));\n return path.join(appData, \"Code\", \"User\", \"chatLanguageModels.json\");\n }\n if (platform === \"darwin\") {\n return path.join(homeDir, \"Library\", \"Application Support\", \"Code\", \"User\", \"chatLanguageModels.json\");\n }\n return path.join(homeDir, \".config\", \"Code\", \"User\", \"chatLanguageModels.json\");\n}\n\nfunction resolveModelsFromTokenStore(tokenStore) {\n if (!tokenStore || typeof tokenStore.listProviders !== \"function\") return [];\n const providers = tokenStore.listProviders();\n const seen = new Set();\n const models = [];\n const now = Math.floor(Date.now() / 1000);\n for (const provider of providers) {\n const modelId = String(provider.default_model || \"\").trim();\n if (!modelId || seen.has(modelId)) continue;\n seen.add(modelId);\n models.push({\n id: modelId,\n object: \"model\",\n created: now,\n owned_by: String(provider.provider || provider.id || \"unknown\").trim().toLowerCase(),\n });\n }\n return models;\n}\n\nfunction writeVscodeChatSettings(options = {}) {\n const env = options.env || process.env;\n const stdout = options.stdout || process.stdout;\n const modelsFile = resolveVscodeChatModelsFile(env);\n const modelsDir = path.dirname(modelsFile);\n\n const proxyEnvSource = env.LLMPROXY_HOME && options.port == null\n ? { ...env, PORT: \"\" }\n : env;\n const baseUrl = getProxyBaseUrl({\n env: proxyEnvSource,\n dataRoot: env.LLMPROXY_HOME || options.dataRoot,\n host: options.host,\n port: options.port,\n });\n const openaiUrl = `${baseUrl}/v1`;\n\n const models = options.availableModels && Array.isArray(options.availableModels)\n ? options.availableModels\n : resolveModelsFromTokenStore(options.tokenStore);\n\n let existing = [];\n if (fs.existsSync(modelsFile)) {\n try {\n const raw = JSON.parse(fs.readFileSync(modelsFile, \"utf8\"));\n existing = Array.isArray(raw) ? raw : [];\n } catch (_parseError) {\n existing = [];\n }\n }\n\n const modelEntries = {};\n for (const model of models) {\n const id = String(model.id || \"\").trim();\n if (!id) continue;\n modelEntries[id] = {\n name: String(model.name || id).trim(),\n url: openaiUrl,\n };\n }\n\n const llmproxyEntry = {\n name: \"llmProxy\",\n vendor: \"openai\",\n url: openaiUrl,\n headers: {},\n models: modelEntries,\n };\n\n const next = existing.filter((entry) => {\n if (!entry || typeof entry !== \"object\") return true;\n const name = String(entry.name || \"\").trim();\n const vendor = String(entry.vendor || \"\").trim().toLowerCase();\n return !(name === \"llmProxy\" || name === \"llmproxy\" || (vendor === \"openai\" && name.toLowerCase().includes(\"llmproxy\")));\n });\n next.push(llmproxyEntry);\n\n fs.mkdirSync(modelsDir, { recursive: true });\n fs.writeFileSync(modelsFile, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n stdout.write(`Configurazione VS Code Chat scritta in ${modelsFile}\\n`);\n stdout.write(` URL: ${openaiUrl}\\n`);\n stdout.write(` Modelli: ${models.length} (${Object.keys(modelEntries).join(\", \")})\\n`);\n stdout.write(`Riavvia VS Code e seleziona \"llmProxy\" nel picker della Chat.\\n`);\n return 0;\n}\n\nfunction writeVscodeClaudeSettings(options = {}) {\n const defaultModel = resolveDefaultModel(options.model, options.availableModels);\n return writeClaudeSettings({\n cwd: options.cwd,\n env: options.env,\n stdout: options.stdout,\n model: defaultModel,\n includeDefaultModel: false,\n omitAuthToken: true,\n uiModelLabel: \"llmProxy\",\n dataRoot: options.dataRoot,\n });\n}\n\nfunction configureClaudeSettings(options = {}) {\n const defaultModel = resolveDefaultModel(options.model, options.availableModels);\n return writeClaudeSettings({\n cwd: options.cwd,\n env: options.env,\n stdout: options.stdout,\n model: defaultModel,\n includeDefaultModel: false,\n omitAuthToken: true,\n uiModelLabel: \"llmProxy\",\n dataRoot: options.dataRoot,\n });\n}\n\nfunction setClaudeModel(options = {}) {\n const selectedModel = String(options.model || \"\").trim();\n if (!selectedModel) {\n throw new Error(\"Uso: llmproxy model:set \");\n }\n return writeClaudeSettings({\n cwd: options.cwd,\n env: options.env,\n stdout: options.stdout,\n model: selectedModel,\n uiModelLabel: selectedModel,\n });\n}\n\nfunction resolveConfigScopeFromParsed(parsed, key = \"\") {\n const wantsProject = Boolean(parsed.flags.project);\n const wantsService = Boolean(parsed.flags.service);\n const wantsGlobal = Boolean(parsed.flags.global);\n const rawScope = String(parsed.flags.scope || \"\").trim().toLowerCase();\n if (rawScope && ![\"project\", \"global\", \"service\"].includes(rawScope)) {\n throw new Error(\"Scope non valido. Usa uno tra: project, global, service\");\n }\n const explicitScope = wantsProject\n ? \"project\"\n : rawScope === \"project\"\n ? \"project\"\n : wantsGlobal\n ? \"global\"\n : rawScope === \"global\"\n ? \"global\"\n : wantsService\n ? \"service\"\n : rawScope === \"service\"\n ? \"service\"\n : \"\";\n if (!key) return explicitScope;\n return inferScopeFromKey(key, explicitScope);\n}\n\nfunction resolveConfigListScope(parsed, cwd, env = process.env) {\n const explicitScope = resolveConfigScopeFromParsed(parsed);\n if (explicitScope) return explicitScope;\n if (hasLocalClaudeFolder(cwd)) return \"\";\n return fs.existsSync(getGlobalClaudeSettingsFile(env)) ? \"\" : \"service\";\n}\n\nfunction persistServiceEnvironmentConfig(serviceConfigFile, environment = {}) {\n if (!serviceConfigFile) return;\n writeServiceConfig(serviceConfigFile, { env: { ...environment } });\n}\n\nfunction writeForegroundRunState(filePath, payload = {}) {\n if (!filePath) return;\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\\n`, \"utf8\");\n}\n\nfunction readForegroundRunState(filePath) {\n if (!filePath || !fs.existsSync(filePath)) return null;\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf8\"));\n } catch {\n return null;\n }\n}\n\nfunction removeForegroundRunState(filePath) {\n if (!filePath) return;\n try {\n fs.unlinkSync(filePath);\n } catch {\n // ignore\n }\n}\n\nfunction stopForegroundDevInstance(options = {}) {\n const {\n paths,\n stdout = process.stdout,\n stderr = process.stderr,\n execCommand,\n killProcess,\n } = options;\n const devPort = 5045;\n const state = readForegroundRunState(paths.foregroundRunPidFile);\n const listeners = listPortListeners({ port: devPort, execCommand });\n const statePid = Number(state?.pid || 0);\n const listenerByStatePid = statePid > 0\n ? listeners.find((listener) => Number(listener.pid) === statePid)\n : null;\n\n const killFn = typeof killProcess === \"function\" ? killProcess : process.kill.bind(process);\n\n if (listenerByStatePid) {\n killFn(listenerByStatePid.pid, \"SIGTERM\");\n removeForegroundRunState(paths.foregroundRunPidFile);\n stdout.write(`Istanza dev fermata su http://127.0.0.1:${devPort}\\n`);\n return 0;\n }\n\n const fallbackListeners = listeners.filter((listener) => /node|bun|deno/i.test(String(listener.command || \"\")));\n if (fallbackListeners.length === 1) {\n killFn(fallbackListeners[0].pid, \"SIGTERM\");\n removeForegroundRunState(paths.foregroundRunPidFile);\n stdout.write(`Istanza dev fermata su http://127.0.0.1:${devPort}\\n`);\n return 0;\n }\n\n if (listeners.length === 0) {\n removeForegroundRunState(paths.foregroundRunPidFile);\n stdout.write(`Nessuna istanza dev attiva su http://127.0.0.1:${devPort}\\n`);\n return 0;\n }\n\n const summary = listeners.map((listener) => `${listener.command || \"unknown\"} pid=${listener.pid}`).join(\", \");\n stderr.write(`Porta ${devPort} occupata da processi non riconosciuti come run locale llmproxy: ${summary}\\n`);\n return 1;\n}\n\nfunction normalizeServiceRuntimeTarget(value, targetPlatform = process.platform) {\n const normalized = String(value || \"\").trim().toLowerCase();\n if (!normalized) return \"\";\n if (normalized === \"docker\") return \"docker\";\n if ([\"native\", \"launchd\", \"systemd\", \"windows\"].includes(normalized)) {\n if (normalized === \"launchd\" && targetPlatform !== \"darwin\") {\n throw new Error(\"`launchd` e' valido solo su macOS.\");\n }\n if (normalized === \"systemd\" && targetPlatform !== \"linux\") {\n throw new Error(\"`systemd` e' valido solo su Linux.\");\n }\n if (normalized === \"windows\" && targetPlatform !== \"win32\") {\n throw new Error(\"`windows` e' valido solo su Windows.\");\n }\n return \"native\";\n }\n throw new Error(\"Runtime non valido. Usa uno tra: docker, native, launchd, systemd.\");\n}\n\nfunction removeNativeServiceArtifacts({ targetPlatform, paths, env, managerOverride }) {\n const nativeEnv = { ...env, LLMPROXY_SERVICE_RUNTIME: \"native\" };\n const manager = managerOverride || createServiceManager(resolveCliServiceManagerOptions({ env: nativeEnv, paths, targetPlatform }));\n manager.stop();\n const serviceFile = targetPlatform === \"darwin\"\n ? paths.launchAgentFile\n : targetPlatform === \"win32\"\n ? paths.serviceRunnerFile\n : paths.systemdUnitFile;\n try { fs.unlinkSync(serviceFile); } catch { /* ignore */ }\n if (targetPlatform === \"win32\") {\n try {\n spawnSync(\"sc.exe\", [\"delete\", \"llmproxy\"], { encoding: \"utf8\", shell: true });\n } catch { /* ignore */ }\n }\n}\n\nfunction removeDockerRuntime({ commandRunner, env, paths }) {\n const composeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(paths.packageRoot, \"docker-compose.production.yml\")).trim();\n const composeEnv = resolveServiceEnvironment({ env, paths, dockerComposeFile: composeFile });\n const spawnOptions = { env: { ...process.env, ...composeEnv } };\n runDockerCompose(commandRunner, composeFile, [\"down\", \"--remove-orphans\"], { spawnOptions });\n}\n\nfunction formatConfigList(entries, scope = \"\") {\n const normalizedScope = String(scope || \"\").trim();\n if (normalizedScope === \"project\") {\n return `Project configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"project\"))}\\n\\nGlobal Claude configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"global\"))}`;\n }\n if (normalizedScope === \"global\") {\n return `Global Claude configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"global\"))}`;\n }\n if (normalizedScope === \"service\") {\n return `Service configuration:\\n${formatConfigEntries(entries)}`;\n }\n return `Project configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"project\"))}\\n\\nGlobal Claude configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"global\"))}\\n\\nService configuration:\\n${formatConfigEntries(entries.filter((entry) => entry.scope === \"service\"))}`;\n}\n\nfunction buildRestCommandRequest(parsed, options = {}) {\n const cwd = path.resolve(String(options.cwd || process.cwd()));\n const headers = { \"content-type\": \"application/json\", \"x-project-path\": cwd };\n const body = {};\n const query = new URLSearchParams();\n const command = parsed.command;\n\n switch (command) {\n case \"version\":\n return { method: \"GET\", path: \"/api/version\" };\n case \"help\":\n if (parsed.args[0]) query.set(\"command\", SHORT_COMMAND_ALIASES[parsed.args[0]] || parsed.args[0]);\n return { method: \"GET\", path: `/api/help${query.toString() ? `?${query.toString()}` : \"\"}` };\n case \"setup\":\n return { method: \"GET\", path: \"/api/setup\" };\n case \"release-notes\":\n if (parsed.flags.version || parsed.args[0]) query.set(\"version\", String(parsed.flags.version || parsed.args[0]).trim());\n if (parsed.flags.locale) query.set(\"locale\", String(parsed.flags.locale).trim());\n return { method: \"GET\", path: `/api/release-notes${query.toString() ? `?${query.toString()}` : \"\"}` };\n case \"login\":\n return { method: \"POST\", path: \"/api/auth/login\" };\n case \"logout\":\n return { method: \"POST\", path: \"/api/auth/logout\" };\n case \"status\":\n return { method: \"GET\", path: \"/api/service/status\" };\n case \"logs\":\n if (parsed.follow) return null;\n return { method: \"GET\", path: \"/api/logs\" };\n case \"models:list\":\n return { method: \"GET\", path: \"/api/models\" };\n case \"claude:setup\":\n if (parsed.flags.model) body.model = String(parsed.flags.model).trim();\n body.projectPath = cwd;\n return { method: \"POST\", path: \"/api/claude/setup\", headers, body };\n case \"pi:setup\":\n body.projectPath = cwd;\n return { method: \"POST\", path: \"/api/pi/setup\", headers, body };\n case \"vscode-chat:setup\":\n body.projectPath = cwd;\n return { method: \"POST\", path: \"/api/vscode/chat/setup\", headers, body };\n case \"vscode-claude:setup\":\n if (parsed.flags.model) body.model = String(parsed.flags.model).trim();\n body.projectPath = cwd;\n return { method: \"POST\", path: \"/api/vscode/claude/setup\", headers, body };\n case \"model:set\":\n body.model = String(parsed.args[0] || \"\").trim();\n body.projectPath = cwd;\n return { method: \"POST\", path: \"/api/model/set\", headers, body };\n case \"provider:add\": {\n const providerId = normalizeKnownProviderId(parsed.args[0]);\n if (!providerId) return null;\n const apiKey = String(parsed.flags[\"api-key\"] || \"\").trim();\n const providerName = String(parsed.flags.name || providerId).trim();\n const model = String(parsed.flags.model || parsed.flags[\"default-model\"] || \"\").trim();\n const plan = String(parsed.flags.plan || \"\").trim();\n const freeModel = parsed.flags[\"free-model\"];\n if (apiKey) {\n return {\n method: \"POST\",\n path: `/api/providers/${encodeURIComponent(providerId)}/api-key`,\n headers,\n body: {\n name: providerName,\n apiKey,\n model,\n plan,\n vision: parsed.flags.vision,\n freeModel,\n },\n };\n }\n return {\n method: \"POST\",\n path: `/api/providers/${encodeURIComponent(providerId)}/login`,\n headers,\n body: {\n name: providerName,\n model,\n plan,\n freeModel,\n },\n };\n }\n case \"provider:key\": {\n const providerId = normalizeKnownProviderId(parsed.args[0]);\n if (!providerId) return null;\n return {\n method: \"POST\",\n path: `/api/providers/${encodeURIComponent(providerId)}/api-key`,\n headers,\n body: {\n name: providerId,\n apiKey: String(parsed.flags[\"api-key\"] || \"\").trim(),\n model: String(parsed.flags.model || parsed.flags[\"default-model\"] || \"\").trim(),\n plan: String(parsed.flags.plan || \"\").trim(),\n vision: parsed.flags.vision,\n freeModel: parsed.flags[\"free-model\"],\n },\n };\n }\n case \"provider:list\":\n return { method: \"GET\", path: \"/api/providers\", headers };\n case \"provider:available\":\n return { method: \"GET\", path: \"/api/providers/available\" };\n case \"provider:status\":\n return { method: \"GET\", path: \"/api/providers/status\" };\n case \"provider:usage\":\n return { method: \"GET\", path: \"/api/providers/usage\" };\n case \"provider:order\":\n return {\n method: \"POST\",\n path: \"/api/providers/order\",\n headers,\n body: {\n id: String(parsed.args[0] || \"\").trim(),\n position: String(parsed.args[1] || \"\").trim(),\n },\n };\n case \"provider:reorder\":\n return {\n method: \"POST\",\n path: \"/api/providers/reorder\",\n headers,\n body: {},\n };\n case \"provider:rename\":\n return {\n method: \"POST\",\n path: `/api/providers/${encodeURIComponent(String(parsed.args[0] || \"\").trim())}/rename`,\n headers,\n body: { name: parsed.args.slice(1).join(\" \").trim() },\n };\n case \"provider:update\": {\n const providerId = String(parsed.args[0] || \"\").trim();\n const flags = parsed.flags || {};\n const patch = {};\n if (flags.vision !== undefined && flags.vision !== null && flags.vision !== \"\") patch.vision = flags.vision;\n if (flags[\"free-model\"] !== undefined && flags[\"free-model\"] !== null && flags[\"free-model\"] !== \"\") patch.free_model = flags[\"free-model\"];\n if (flags.name !== undefined && flags.name !== null && flags.name !== \"\") patch.name = String(flags.name).trim();\n return {\n method: \"PATCH\",\n path: `/api/providers/${encodeURIComponent(providerId)}`,\n headers,\n body: { patch },\n };\n }\n case \"provider:remove\":\n return {\n method: \"DELETE\",\n path: `/api/providers/${encodeURIComponent(String(parsed.args[0] || \"\").trim())}`,\n headers,\n };\n case \"service:stop\":\n return { method: \"POST\", path: \"/api/service/stop\" };\n case \"service:restart\":\n return { method: \"POST\", path: \"/api/service/restart\" };\n case \"service:runtime\":\n body.runtime = String(parsed.args[0] || \"\").trim();\n return { method: \"POST\", path: \"/api/service/runtime\", body };\n case \"stats\":\n return { method: \"GET\", path: \"/api/stats\" };\n case \"update\":\n return { method: \"POST\", path: \"/api/update\" };\n case \"uninstall\":\n return { method: \"POST\", path: \"/api/uninstall\" };\n case \"config:list\": {\n const scope = resolveConfigScopeFromParsed(parsed);\n if (scope) query.set(\"scope\", scope);\n query.set(\"projectPath\", cwd);\n return { method: \"GET\", path: `/api/config${query.toString() ? `?${query.toString()}` : \"\"}`, headers };\n }\n case \"config:get\": {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key) return null;\n const scope = resolveConfigScopeFromParsed(parsed, key);\n if (scope) query.set(\"scope\", scope);\n query.set(\"projectPath\", cwd);\n return { method: \"GET\", path: `/api/config/${encodeURIComponent(key)}?${query.toString()}`, headers };\n }\n case \"config:set\": {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key) return null;\n const scope = resolveConfigScopeFromParsed(parsed, key);\n return {\n method: \"POST\",\n path: `/api/config/${encodeURIComponent(key)}`,\n headers,\n body: {\n value: String(parsed.args[1] || \"\"),\n scope,\n projectPath: cwd,\n },\n };\n }\n case \"config:unset\": {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key) return null;\n const scope = resolveConfigScopeFromParsed(parsed, key);\n return {\n method: \"DELETE\",\n path: `/api/config/${encodeURIComponent(key)}`,\n headers,\n body: {\n scope,\n projectPath: cwd,\n },\n };\n }\n case \"proxy:add\": {\n const url = String(parsed.args[0] || \"\").trim();\n if (!url) return null;\n return { method: \"POST\", path: \"/api/proxies\", headers, body: { url } };\n }\n case \"proxy:list\":\n return { method: \"GET\", path: \"/api/proxies\", headers };\n case \"proxy:remove\":\n return { method: \"DELETE\", path: `/api/proxies/${encodeURIComponent(String(parsed.args[0] || \"\").trim())}`, headers };\n case \"proxy:reorder\":\n return { method: \"POST\", path: \"/api/proxies/reorder\", headers, body: {} };\n case \"proxy:test\":\n return { method: \"POST\", path: \"/api/proxies/test\", headers };\n default:\n return null;\n }\n}\n\nasync function tryRunViaRest(parsed, options = {}) {\n if (options.preferLocalExecution) return null;\n if ([\n \"version\",\n \"status\",\n \"config:list\",\n \"config:get\",\n \"config:set\",\n \"config:unset\",\n \"update\",\n \"uninstall\",\n \"service:stop\",\n \"service:restart\",\n \"service:runtime\",\n ].includes(parsed.command)) return null;\n if (options.tokenStore || options.modelCatalogStore || options.serviceManager || options.commandRunner) {\n return null;\n }\n let request = null;\n try {\n request = buildRestCommandRequest(parsed, options);\n } catch {\n return null;\n }\n if (!request) return null;\n\n const restFetchFn = options.restFetchFn === undefined ? fetch : options.restFetchFn;\n if (typeof restFetchFn !== \"function\") return null;\n\n const baseUrl = getProxyBaseUrl({\n env: options.env || process.env,\n dataRoot: options.dataRoot,\n });\n\n try {\n const healthResponse = await restFetchFn(`${baseUrl}/health`);\n if (!healthResponse?.ok) return null;\n\n const response = await restFetchFn(`${baseUrl}${request.path}`, {\n method: request.method,\n headers: request.headers,\n body: request.body ? JSON.stringify(request.body) : undefined,\n });\n const payload = await response.json();\n const data = payload?.data || {};\n if (data.output) {\n options.stdout.write(`${String(data.output).trimEnd()}\\n`);\n }\n if (data.error) {\n options.stderr.write(`${String(data.error).trimEnd()}\\n`);\n }\n return Number.isFinite(Number(payload?.exitCode)) ? Number(payload.exitCode) : (payload?.success ? 0 : 1);\n } catch {\n return null;\n }\n}\n\nfunction tailFile(filePath, lines = 80) {\n if (!fs.existsSync(filePath)) return \"\";\n const content = fs.readFileSync(filePath, \"utf8\").trimEnd();\n if (!content) return \"\";\n const chunks = content.split(/\\r?\\n/);\n return chunks.slice(-lines).join(\"\\n\");\n}\n\nfunction tailLatestRequestLog(logsDir, lines = 80) {\n if (!fs.existsSync(logsDir)) return \"\";\n const requestLogs = fs.readdirSync(logsDir)\n .filter((fileName) => /^requests-\\d{4}-\\d{2}-\\d{2}\\.jsonl$/.test(fileName))\n .sort();\n\n const latestLog = requestLogs.at(-1);\n if (!latestLog) return \"\";\n return tailFile(path.join(logsDir, latestLog), lines);\n}\n\nfunction readLatestRequestSummary(logsDir, sinceIso = null) {\n if (!fs.existsSync(logsDir)) return null;\n const requestLogs = fs.readdirSync(logsDir)\n .filter((fileName) => /^requests-\\d{4}-\\d{2}-\\d{2}\\.jsonl(?:\\.\\d+)?$/.test(fileName))\n .sort();\n\n let latest = null;\n for (const fileName of requestLogs) {\n const filePath = path.join(logsDir, fileName);\n let content = \"\";\n try {\n content = fs.readFileSync(filePath, \"utf8\");\n } catch {\n continue;\n }\n for (const line of content.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n let entry;\n try {\n entry = JSON.parse(trimmed);\n } catch {\n continue;\n }\n if (entry?.event !== \"request_summary\") continue;\n const ts = String(entry?.ts || \"\").trim();\n if (!ts) continue;\n if (sinceIso && ts < sinceIso) continue;\n if (!latest || ts >= String(latest.ts || \"\")) latest = entry;\n }\n }\n return latest;\n}\n\nasync function waitForLatestRequestSummary(logsDir, options = {}) {\n const sinceIso = options.sinceIso || null;\n const matcher = typeof options.matcher === \"function\" ? options.matcher : null;\n const retries = Number.isInteger(options.retries) ? options.retries : 40;\n const delayMs = Number.isFinite(options.delayMs) ? options.delayMs : 50;\n const sleepFn = typeof options.sleepFn === \"function\"\n ? options.sleepFn\n : (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n\n for (let retry = 0; retry < retries; retry += 1) {\n const summary = readLatestRequestSummary(logsDir, sinceIso);\n if (summary && (!matcher || matcher(summary))) return summary;\n if (retry < retries - 1) await sleepFn(delayMs);\n }\n return null;\n}\n\nfunction formatInferenceProviderAttempt(attempt = {}) {\n const provider = String(attempt.provider || \"unknown\").trim();\n const model = String(attempt.actual_model || attempt.effective_model || \"\").trim();\n if (attempt.success === true) {\n return `${provider}[ok${model ? ` ${model}` : \"\"}]`;\n }\n if (typeof attempt.status === \"number\") {\n return `${provider}[HTTP ${attempt.status}${model ? ` ${model}` : \"\"}]`;\n }\n return `${provider}[fail${model ? ` ${model}` : \"\"}]`;\n}\n\nfunction normalizeInferenceDisplayModel(model) {\n const raw = String(model || \"\").trim();\n if (!raw) return \"\";\n const leaf = raw.includes(\"/\") ? raw.split(\"/\").pop() : raw;\n return leaf.replace(/-\\d{8}$/i, \"\");\n}\n\nfunction buildRemainingFallbackProviders(providers, finalProviderId, finalModel) {\n const orderedProviders = Array.isArray(providers) ? providers.slice() : [];\n if (orderedProviders.length === 0) return [];\n\n const normalizedFinalProviderId = String(finalProviderId || \"\").trim().toLowerCase();\n const normalizedFinalModel = String(finalModel || \"\").trim().toLowerCase();\n let winnerIndex = orderedProviders.findIndex((provider) => {\n const providerId = String(provider?.id || \"\").trim().toLowerCase();\n const model = String(provider?.effective_model || provider?.default_model || \"\").trim().toLowerCase();\n return providerId === normalizedFinalProviderId && (!normalizedFinalModel || model === normalizedFinalModel);\n });\n\n if (winnerIndex === -1) {\n winnerIndex = orderedProviders.findIndex((provider) => String(provider?.id || \"\").trim().toLowerCase() === normalizedFinalProviderId);\n }\n if (winnerIndex === -1) return [];\n return orderedProviders.slice(winnerIndex + 1);\n}\n\nfunction formatOrdinalLabel(position) {\n const index = Number(position || 0);\n if (index === 1) return \"1st\";\n if (index === 2) return \"2nd\";\n if (index === 3) return \"3rd\";\n return `${index}th`;\n}\n\nfunction followLogs(paths, stdout, stderr) {\n const requestLogs = fs.existsSync(paths.logsDir)\n ? fs.readdirSync(paths.logsDir)\n .filter((fileName) => /^requests-\\d{4}-\\d{2}-\\d{2}\\.jsonl$/.test(fileName))\n .sort()\n .map((fileName) => path.join(paths.logsDir, fileName))\n : [];\n const files = [paths.stdoutLogFile, paths.stderrLogFile, ...requestLogs].filter((filePath) => fs.existsSync(filePath));\n if (files.length === 0) {\n stdout.write(\"Nessun file di log disponibile. Avvia prima il servizio.\\n\");\n return Promise.resolve(0);\n }\n\n return new Promise((resolve, reject) => {\n const child = spawn(\"tail\", [\"-n\", \"50\", \"-f\", ...files], {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n child.stdout.on(\"data\", (chunk) => stdout.write(chunk));\n child.stderr.on(\"data\", (chunk) => stderr.write(chunk));\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => resolve(code ?? 0));\n });\n}\n\nfunction isTransientLocalFetchError(error) {\n const message = String(error?.message || error || \"\").trim();\n return /fetch failed|socket connection was closed unexpectedly|socket.*closed|econnreset|econnrefused|etimedout|timeout|network error/i.test(message);\n}\n\nasync function runLocalProxyTestRequest(fetchFn, url, request, sleepFn) {\n const delay = typeof sleepFn === \"function\"\n ? sleepFn\n : (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n\n for (let attempt = 0; attempt < 4; attempt += 1) {\n try {\n return await fetchFn(url, request);\n } catch (error) {\n if (!isTransientLocalFetchError(error) || attempt === 3) throw error;\n await delay(250);\n }\n }\n}\n\nasync function withTemporaryProviderProxy(providerStore, provider, proxyUrl, fn) {\n if (!providerStore || !provider || !provider.id || !proxyUrl) return fn(provider);\n\n const originalProvider = providerStore.getProvider(provider.id);\n if (!originalProvider) return fn(provider);\n\n providerStore.saveProvider(provider.id, {\n ...originalProvider,\n proxy_url: proxyUrl,\n proxy_rotation: false,\n }, { name: originalProvider.name });\n\n try {\n return await fn({\n ...provider,\n ...originalProvider,\n proxy_url: proxyUrl,\n proxy_rotation: false,\n });\n } finally {\n providerStore.saveProvider(provider.id, originalProvider, { name: originalProvider.name });\n }\n}\n\nfunction extractAssistantTextFromPayload(payload) {\n if (payload?.choices?.[0]?.message) {\n const message = payload.choices[0].message;\n const content = message?.content;\n if (typeof content === \"string\") return content.trim();\n if (Array.isArray(content)) {\n return content\n .map((item) => {\n if (typeof item === \"string\") return item;\n if (item && typeof item.text === \"string\") return item.text;\n return \"\";\n })\n .filter(Boolean)\n .join(\"\\n\")\n .trim();\n }\n }\n\n if (Array.isArray(payload?.content)) {\n return payload.content\n .filter((item) => item && item.type === \"text\" && typeof item.text === \"string\")\n .map((item) => item.text)\n .join(\"\\n\")\n .trim();\n }\n\n return \"\";\n}\n\nasync function readResponseErrorSnippet(response) {\n if (!response) return \"\";\n try {\n if (typeof response.text !== \"function\") return \"\";\n const rawText = String(await response.text() || \"\").trim();\n if (!rawText) return \"\";\n try {\n const parsed = JSON.parse(rawText);\n const nestedMessage = parsed?.error?.message\n || parsed?.error?.error\n || parsed?.message\n || parsed?.detail\n || parsed?.details;\n const normalizedNested = typeof nestedMessage === \"string\"\n ? nestedMessage.trim()\n : nestedMessage != null\n ? JSON.stringify(nestedMessage)\n : \"\";\n if (normalizedNested) return normalizedNested.slice(0, 240);\n } catch {\n // fall back to the raw text\n }\n return rawText.replace(/\\s+/g, \" \").slice(0, 240);\n } catch {\n return \"\";\n }\n}\n\nasync function probeProviderForTest({ fetchFn, proxyBaseUrl, provider, sleepFn, logsDir }) {\n const model = provider.default_model || (String(provider.provider || \"copilot\") === \"copilot\" ? DEFAULT_COPILOT_MODEL : \"\");\n if (!model) {\n return {\n ok: false,\n kind: \"missing-model\",\n message: \"default model mancante\",\n model,\n displayModel: model,\n };\n }\n\n const providerStartedAtIso = new Date().toISOString();\n const response = await runLocalProxyTestRequest(\n fetchFn,\n `${proxyBaseUrl}/v1/messages`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n provider: provider.id === \"auto\" ? undefined : provider.id,\n model,\n stream: false,\n max_tokens: 1024,\n messages: [\n {\n role: \"user\",\n content: [{ type: \"text\", text: `Rispondi solo: llmproxy-test-${provider.id}` }],\n },\n ],\n }),\n },\n sleepFn,\n );\n\n if (!response.ok && response.status !== 429 && response.status !== 402) {\n const errorSnippet = await readResponseErrorSnippet(response);\n return {\n ok: false,\n kind: \"http\",\n status: response.status,\n message: errorSnippet ? `HTTP ${response.status}: ${errorSnippet}` : `HTTP ${response.status}`,\n model,\n displayModel: model,\n };\n }\n\n if (!response.ok && (response.status === 429 || response.status === 402)) {\n return {\n ok: true,\n model,\n displayModel: model,\n finalProvider: provider.id,\n finalModel: model,\n degradedStatus: response.status,\n };\n }\n\n const payload = await response.json();\n const assistantText = Array.isArray(payload?.content)\n ? payload.content\n .filter((item) => item && item.type === \"text\" && typeof item.text === \"string\")\n .map((item) => item.text)\n .join(\"\\n\")\n .trim()\n : \"\";\n const visibleAssistantText = sanitizeTestAssistantText(assistantText);\n let summary = readLatestRequestSummary(logsDir, providerStartedAtIso);\n if (summary && !requestSummaryMatchesProvider(summary, provider)) {\n summary = null;\n }\n if (!visibleAssistantText && !(summary?.success && requestSummaryMatchesProvider(summary, provider))) {\n summary = await waitForLatestRequestSummary(logsDir, {\n sinceIso: providerStartedAtIso,\n sleepFn,\n matcher: (entry) => entry?.success === true && requestSummaryMatchesProvider(entry, provider),\n });\n }\n const finalProvider = String(summary?.finalProvider || \"\").trim();\n const finalModel = String(summary?.finalModel || payload?.model || \"\").trim();\n if (!visibleAssistantText && !(summary?.success && finalProvider)) {\n return {\n ok: false,\n kind: \"empty\",\n message: \"risposta vuota\",\n model,\n displayModel: finalModel || model,\n };\n }\n\n return {\n ok: true,\n model,\n displayModel: finalModel || model,\n finalProvider,\n finalModel,\n };\n}\n\nfunction isDockerManagedRuntime(env, paths, targetPlatform) {\n const entryFile = resolveServiceEntryFile({ env, packageRoot: paths.packageRoot, targetPlatform });\n return path.basename(entryFile) === \"docker-launchd-entry.js\";\n}\n\nfunction isDockerServiceRunning(commandRunner, composeFile, serviceName, spawnOptions = {}) {\n const result = runDockerCompose(commandRunner, composeFile, [\"ps\", \"--status\", \"running\", \"--services\", serviceName], { spawnOptions });\n if (!result || result.status !== 0) return false;\n return String(result.stdout || \"\")\n .split(/\\r?\\n/)\n .map((entry) => entry.trim())\n .includes(serviceName);\n}\n\nfunction ensureDockerServiceRunning({ commandRunner, env, paths, targetPlatform, forceRecreate = false }) {\n if (!isDockerManagedRuntime(env, paths, targetPlatform)) {\n return { ok: true, checked: false };\n }\n const composeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(paths.packageRoot, \"docker-compose.production.yml\")).trim();\n const serviceName = String(env.LLMPROXY_DOCKER_SERVICE || \"llmproxy\").trim() || \"llmproxy\";\n const composeEnv = resolveServiceEnvironment({ env, paths, dockerComposeFile: composeFile });\n const spawnOptions = { env: { ...process.env, ...composeEnv } };\n if (!fs.existsSync(composeFile)) {\n return { ok: false, error: `File docker compose non trovato: ${composeFile}` };\n }\n try {\n fs.accessSync(composeFile, fs.constants.R_OK);\n } catch {\n const installDir = path.dirname(composeFile);\n return {\n ok: false,\n error: `Permesso negato nella lettura di ${composeFile}. Esegui: sudo chmod -R a+rX \"${installDir}\"`,\n };\n }\n const dockerCompose = resolveDockerComposeCommand(commandRunner, { spawnOptions });\n if (!dockerCompose.dockerAvailable) {\n return { ok: false, error: \"Docker non trovato nel PATH.\" };\n }\n if (!dockerCompose.ok) {\n return { ok: false, error: \"Docker Compose non e' disponibile (`docker compose` e `docker-compose` falliscono).\" };\n }\n const alreadyRunning = isDockerServiceRunning(commandRunner, composeFile, serviceName, spawnOptions);\n if (alreadyRunning && !forceRecreate) {\n return { ok: true, checked: true, restarted: false, composeLabel: dockerCompose.label };\n }\n const upArgs = forceRecreate\n ? [\"up\", \"-d\", \"--build\", \"--force-recreate\", serviceName]\n : [\"up\", \"-d\", \"--build\", serviceName];\n const upResult = runDockerCompose(commandRunner, composeFile, upArgs, { invocation: dockerCompose, spawnOptions });\n if (!upResult || upResult.status !== 0) {\n return {\n ok: false,\n error: String(upResult?.stderr || upResult?.stdout || `${dockerCompose.label} up failed`).trim(),\n };\n }\n if (!isDockerServiceRunning(commandRunner, composeFile, serviceName, spawnOptions)) {\n return { ok: false, error: `Il container Docker ${serviceName} non risulta attivo dopo il restart.` };\n }\n return { ok: true, checked: true, restarted: true, composeLabel: dockerCompose.label };\n}\n\nfunction getManagedServiceStatus({ commandRunner, env, paths, targetPlatform, serviceManager }) {\n if (!isDockerManagedRuntime(env, paths, targetPlatform)) {\n return {\n kind: serviceManager.kind,\n status: serviceManager.status(),\n };\n }\n\n const composeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(paths.packageRoot, \"docker-compose.production.yml\")).trim();\n const serviceName = String(env.LLMPROXY_DOCKER_SERVICE || \"llmproxy\").trim() || \"llmproxy\";\n const composeEnv = resolveServiceEnvironment({ env, paths, dockerComposeFile: composeFile });\n const spawnOptions = { env: { ...process.env, ...composeEnv } };\n const dockerCompose = resolveDockerComposeCommand(commandRunner, { spawnOptions });\n\n if (!dockerCompose.dockerAvailable) {\n return {\n kind: \"docker\",\n status: { ok: false, active: false, stdout: \"\", stderr: \"Docker non trovato nel PATH.\" },\n };\n }\n\n if (!dockerCompose.ok) {\n return {\n kind: \"docker\",\n status: { ok: false, active: false, stdout: \"\", stderr: \"Docker Compose non e' disponibile (`docker compose` e `docker-compose` falliscono).\" },\n };\n }\n\n const active = isDockerServiceRunning(commandRunner, composeFile, serviceName, spawnOptions);\n return {\n kind: \"docker\",\n status: {\n ok: true,\n active,\n stdout: active ? `${serviceName} container active via ${dockerCompose.label}` : `${serviceName} container not running`,\n stderr: \"\",\n },\n };\n}\n\nasync function waitForLocalProxyHealth({ fetchFn, baseUrl, sleepFn, retries = 20, delayMs = 250 }) {\n const delay = typeof sleepFn === \"function\"\n ? sleepFn\n : (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n let lastError = null;\n\n for (let attempt = 0; attempt < retries; attempt += 1) {\n try {\n const response = await fetchFn(`${baseUrl}/health`, {\n method: \"GET\",\n headers: { \"content-type\": \"application/json\" },\n });\n if (response.ok) {\n const payload = await response.json();\n if (payload?.ok !== false) return { ok: true, payload };\n }\n lastError = new Error(`health HTTP ${response.status}`);\n } catch (error) {\n lastError = error;\n }\n await delay(delayMs);\n }\n\n return {\n ok: false,\n error: lastError ? String(lastError.message || lastError) : \"health check failed\",\n };\n}\n\nasync function runCli(argv, options = {}) {\n const stdout = options.stdout || process.stdout;\n const stderr = options.stderr || process.stderr;\n const fetchFn = options.fetchFn || fetch;\n const sleep = options.sleep;\n const parsed = parseArgs(argv || process.argv);\n const targetPlatform = options.platform || process.platform;\n const env = loadRuntimeEnv({\n env: options.env || process.env,\n packageRoot: options.packageRoot,\n dataRoot: options.dataRoot,\n homeDir: options.homeDir,\n platform: targetPlatform,\n });\n const paths = createPaths({ dataRoot: options.dataRoot, packageRoot: options.packageRoot, env, homeDir: options.homeDir, platform: targetPlatform });\n ensureRuntimeDirs(paths);\n const restExitCode = await tryRunViaRest(parsed, {\n ...options,\n env,\n dataRoot: paths.dataRoot,\n stdout,\n stderr,\n });\n if (restExitCode !== null) {\n return restExitCode;\n }\n const tokenStore = options.tokenStore || createTokenStore({ filePath: paths.tokenFile });\n const providerRegistry = options.providerRegistry || createProviderRegistry({\n filePath: paths.providerRegistryFile,\n secret: env.LLMPROXY_SECRET || null,\n });\n const proxyStore = options.proxyStore || require(\"./proxy-store\").createProxyStore({\n filePath: paths.proxyRegistryFile,\n });\n const providerStore = tokenStore;\n const modelCatalogStore = options.modelCatalogStore || createCopilotModelCatalogStore({ filePath: paths.modelCatalogFile });\n const serviceManagerOptions = resolveCliServiceManagerOptions({ env, paths, targetPlatform });\n const serviceManager = options.serviceManager || createServiceManager(serviceManagerOptions);\n\n if (parsed.command === \"help\") {\n if (parsed.args[0]) return printCommandHelp(SHORT_COMMAND_ALIASES[parsed.args[0]] || parsed.args[0], stdout);\n printHelp(stdout);\n return 0;\n }\n\n if (parsed.command === \"setup\") {\n stdout.write(`Runtime root: ${paths.dataRoot}\\n`);\n stdout.write(`Service manager: ${serviceManager.kind}\\n`);\n return 0;\n }\n\n if (parsed.command === \"version\") {\n stdout.write(`${readPackageVersion(paths.packageRoot)}\\n`);\n return 0;\n }\n\n if (parsed.command === \"release-notes\") {\n const version = String(parsed.flags.version || parsed.args[0] || readPackageVersion(paths.packageRoot)).trim();\n const locale = parsed.flags.locale\n ? normalizeLocale(parsed.flags.locale)\n : resolveOutputLocale({ env, paths });\n const commitMessage = decodeBase64Utf8(parsed.flags[\"commit-message-base64\"] || parsed.flags.commitMessageBase64);\n stdout.write(`${formatReleaseNotes(version, locale, { commitMessage, packageRoot: paths.packageRoot })}\\n`);\n return 0;\n }\n\n if (isPersistentInstallCommand(parsed.command)) {\n const installLocale = resolveInstallLocale(parsed.command);\n const isEnglishInstallCommand = installLocale === \"en\";\n if (targetPlatform !== \"darwin\" && targetPlatform !== \"linux\" && targetPlatform !== \"win32\") {\n if (isEnglishInstallCommand) {\n stderr.write(`Unsupported platform for persistent installation: ${targetPlatform}\\n`);\n } else {\n stderr.write(`Piattaforma non supportata per installazione persistente: ${targetPlatform}\\n`);\n }\n return 1;\n }\n\n const preflight = resolvePersistentInstallPrerequisites({\n commandRunner: options.commandRunner,\n env,\n packageRoot: paths.packageRoot,\n targetPlatform,\n });\n if (!preflight.ok) {\n stderr.write(formatPersistentInstallPrerequisitesFailure(preflight, {\n locale: installLocale,\n platform: targetPlatform,\n osRelease: readOsReleaseInfo({ platform: targetPlatform, content: options.osReleaseContent }),\n }));\n return 1;\n }\n\n const result = runPersistentInstall(options.commandRunner, {\n packageRoot: paths.packageRoot,\n platform: targetPlatform,\n locale: installLocale,\n });\n\n if (result?.status === 0) {\n persistServiceEnvironmentConfig(paths.serviceConfigFile, serviceManagerOptions.environment);\n const rawStdout = String(result.stdout || \"\");\n const globalBinMatch = rawStdout.match(/^__LLMPROXY_GLOBAL_BIN__=(.+)$/m);\n const binScopeMatch = rawStdout.match(/^__LLMPROXY_BIN_SCOPE__=(.+)$/m);\n const binHintMatch = rawStdout.match(/^__LLMPROXY_BIN_HINT__=(.+)$/m);\n const visibleStdout = rawStdout\n .replace(/^__LLMPROXY_GLOBAL_BIN__=.+$/m, \"\")\n .replace(/^__LLMPROXY_BIN_SCOPE__=.+$/m, \"\")\n .replace(/^__LLMPROXY_BIN_HINT__=.+$/m, \"\")\n .trim();\n\n const binScope = binScopeMatch?.[1]?.trim() || \"unknown\";\n\n if (isEnglishInstallCommand) {\n stdout.write(\"Persistent installation completed.\\n\");\n if (globalBinMatch?.[1]) stdout.write(`Global binary: ${String(globalBinMatch[1]).trim()}\\n`);\n stdout.write(`Binary scope: ${binScope === \"server\" ? \"server-wide (available to all users)\" : binScope === \"user\" ? \"user-only (current user)\" : binScope}\\n`);\n if (binScope === \"user\" && binHintMatch?.[1]) {\n stdout.write(`Hint: ${binHintMatch[1].trim()}\\n`);\n }\n stdout.write(`Persistent service enabled with ${serviceManager.kind}.\\n`);\n } else {\n stdout.write(\"Installazione persistente completata.\\n\");\n if (globalBinMatch?.[1]) stdout.write(`Binario globale: ${String(globalBinMatch[1]).trim()}\\n`);\n stdout.write(`Visibilità binario: ${binScope === \"server\" ? \"server-wide (disponibile a tutti gli utenti)\" : binScope === \"user\" ? \"solo utente corrente\" : binScope}\\n`);\n if (binScope === \"user\" && binHintMatch?.[1]) {\n stdout.write(`Suggerimento: ${binHintMatch[1].trim()}\\n`);\n }\n stdout.write(`Servizio persistente attivato con ${serviceManager.kind}.\\n`);\n }\n if (visibleStdout) stdout.write(`${visibleStdout}\\n`);\n if (targetPlatform === \"linux\") {\n if (isEnglishInstallCommand) {\n stdout.write(\"Linux note: to restart even without a user login, enable linger with `sudo loginctl enable-linger $USER`.\\n\");\n } else {\n stdout.write(\"Nota Linux: per riavvio anche senza login utente abilita linger con `sudo loginctl enable-linger $USER`.\\n\");\n }\n }\n persistInstallLocale(paths, installLocale);\n return 0;\n }\n\n if (isEnglishInstallCommand) {\n stderr.write((result?.stderr && String(result.stderr).trim()) || \"Persistent installation failed.\\n\");\n } else {\n stderr.write((result?.stderr && String(result.stderr).trim()) || \"Installazione persistente fallita.\\n\");\n }\n return 1;\n }\n\n if (parsed.command === \"models:list\") {\n const showAll = Boolean(parsed.flags.all || parsed.flags[\"all-providers\"]);\n if (showAll) {\n const models = await resolveAvailableCopilotModels({\n tokenStore,\n fetchFn,\n catalogStore: modelCatalogStore,\n fallbackModels: getAvailableModels(),\n });\n stdout.write(`${formatModelList(models)}\\n`);\n } else {\n const providers = providerStore.listProviders();\n if (providers.length === 0) {\n stderr.write(\"Nessun provider configurato. Usa llmproxy provider:add per aggiungerne uno.\\n\");\n return 1;\n }\n const seenModels = new Set();\n const entries = providers.map((p, idx) => {\n const providerId = String(p.id || \"\").trim();\n const modelId = String(p.default_model || \"\").trim();\n const key = modelId ? `${providerId}#${modelId}` : providerId;\n const intelligence = String(p.intelligence_score || p.coding || \"\").trim();\n const credit = String(p.credit || \"\").trim();\n const bench = String(p.bench || \"\").trim();\n return { idx: idx + 1, key, providerId, modelId, intelligence, credit, bench };\n }).filter((e) => {\n if (seenModels.has(e.key)) return false;\n seenModels.add(e.key);\n return true;\n });\n for (const e of entries) {\n const extras = [];\n if (e.credit && e.credit !== \"n/a\") extras.push(`credit=${e.credit}`);\n if (e.intelligence) extras.push(`coding=${e.intelligence}`);\n if (e.bench && !e.bench.includes(\"errore\")) extras.push(`bench=${e.bench}`);\n const extra = extras.length > 0 ? ` (${extras.join(\", \")})` : \"\";\n stdout.write(`${e.idx}. ${e.key}${extra}\\n`);\n }\n }\n return 0;\n }\n\n if (parsed.command === \"test\") {\n const providers = providerStore.listProviders();\n if (providers.length === 0) {\n stderr.write(\"Non ci sono LLM configurati! Usa llmproxy provider:add per configurarne uno.\\n\");\n stderr.write(\" Esempi: llmproxy provider:add copilot\\n\");\n stderr.write(\" llmproxy provider:add openrouter --api-key --model --vision true\\n\");\n stderr.write(\" Provider disponibili: llmproxy provider:available\\n\");\n return 1;\n }\n const inferenceMode = Boolean(parsed.flags.i || parsed.flags.inference);\n const allProviders = Boolean(parsed.flags[\"all-providers\"]);\n const targetProviderArg = inferenceMode ? String(parsed.args[0] || \"\").trim() : \"\";\n let targetProvider = null;\n let targetModelOverride = null;\n if (targetProviderArg) {\n const numericIndex = /^\\d+$/.test(targetProviderArg) ? parseInt(targetProviderArg, 10) : -1;\n if (numericIndex >= 1 && numericIndex <= providers.length) {\n targetProvider = providers[numericIndex - 1];\n } else {\n const byId = providers.find((p) => String(p.id || \"\").toLowerCase() === targetProviderArg.toLowerCase());\n if (byId) {\n targetProvider = byId;\n } else {\n // Se non è un id/indice, trattalo come nome di modello (es. tencent/hy3:free, opencode-bacin2, qwen)\n const byModel = providers.find(\n (p) => String(p.effective_model || p.default_model || \"\").toLowerCase() === targetProviderArg.toLowerCase(),\n );\n if (byModel) {\n targetProvider = byModel;\n targetModelOverride = targetProviderArg;\n }\n }\n }\n if (!targetProvider) {\n stderr.write(`Provider o modello non trovato: ${targetProviderArg}\\nUsa llmproxy provider:list per vedere i provider configurati.\\n`);\n return 1;\n }\n }\n const selectedTargets = inferenceMode\n ? []\n : (allProviders ? providers : providers.slice(0, 1));\n\n // Verifica che il proxy sia raggiungibile prima di iniziare i test\n const proxyBaseUrl = getProxyBaseUrl({ env, dataRoot: paths.dataRoot });\n stdout.write(`Proxy endpoint: ${proxyBaseUrl}\\n`);\n\n try {\n const healthCheck = await fetchFn(`${proxyBaseUrl}/v1/llm/health`, {\n method: \"GET\",\n headers: { \"content-type\": \"application/json\" },\n });\n if (!healthCheck.ok) {\n stderr.write(`Errore: Il proxy non è raggiungibile su ${proxyBaseUrl}\\n`);\n stderr.write(` Status: ${healthCheck.status}\\n`);\n stderr.write(` Verifica che il servizio llmproxy sia in esecuzione:\\n`);\n stderr.write(` llmproxy service:start\\n`);\n stderr.write(` # oppure\\n`);\n stderr.write(` systemctl --user status llmproxy\\n`);\n return 1;\n }\n const healthData = await healthCheck.json();\n stdout.write(`Proxy stato: ${healthData.ok ? 'OK' : 'ERRORE'} (versione: ${healthData.manifest_version || 'sconosciuta'})\\n\\n`);\n } catch (error) {\n stderr.write(`Errore: Il proxy non è raggiungibile su ${proxyBaseUrl}\\n`);\n stderr.write(` ${error.message}\\n`);\n stderr.write(` Verifica che il servizio llmproxy sia in esecuzione:\\n`);\n stderr.write(` llmproxy service:start\\n`);\n stderr.write(` # oppure\\n`);\n stderr.write(` systemctl --user status llmproxy\\n`);\n stderr.write(` Se il servizio è in esecuzione, verifica la porta:\\n`);\n stderr.write(` ss -tlnp | grep node\\n`);\n return 1;\n }\n\n const commandCwd = path.resolve(String(options.cwd || process.cwd()));\n const projectSettings = resolveClaudeProjectSettings(\n commandCwd,\n { env },\n );\n const effectiveStatsKey = String(getScopeValue({\n key: \"LLMPROXY_LLM_STATS_API_KEY\",\n cwd: commandCwd,\n serviceConfigFile: paths.serviceConfigFile,\n env,\n }).value || \"\").trim();\n if (!effectiveStatsKey) {\n stderr.write(`${LLM_STATS_API_KEY_REQUIRED_MESSAGE}\\n`);\n return 1;\n }\n\n let failures = 0;\n\n if (inferenceMode) {\n const infProxyFlag = String(parsed.flags.proxy || \"\").trim();\n const infProviderFlag = String(parsed.flags.provider || \"\").trim();\n const infFetch = infProxyFlag ? makeProxyFetch(fetchFn, infProxyFlag) : fetchFn;\n const infProxyLabel = infProxyFlag ? ` via proxy ${infProxyFlag}` : \"\";\n\n // Se --provider flag specificato, sovrascrive targetProvider\n let infTargetProvider = targetProvider;\n if (infProviderFlag && !targetProvider) {\n const byId = providers.find((p) => String(p.id || \"\").toLowerCase() === infProviderFlag.toLowerCase());\n const byName = providers.find((p) => String(p.name || \"\").toLowerCase() === infProviderFlag.toLowerCase());\n infTargetProvider = byId || byName || null;\n if (!infTargetProvider) {\n stderr.write(`Provider non trovato: ${infProviderFlag}\\n`);\n return 1;\n }\n }\n const startedAtIso = new Date().toISOString();\n const startedAtMs = Date.now();\n try {\n const effectiveProviders = resolveEffectiveProviderList(providers, projectSettings.configuredModel).providers;\n const response = await runLocalProxyTestRequest(\n infFetch,\n `${proxyBaseUrl}/v1/messages`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n stream: false,\n max_tokens: 256,\n ...(targetProvider ? { provider: targetProvider.id } : {}),\n ...(targetModelOverride\n ? { model: targetModelOverride }\n : (targetProvider ? { model: targetProvider.effective_model || targetProvider.default_model } : {})),\n messages: [\n {\n role: \"user\",\n content: [{ type: \"text\", text: \"Rispondi solo: llmproxy-test-inference\" }],\n },\n ],\n }),\n },\n sleep,\n );\n\n if (!response.ok) {\n stdout.write(`inference: fail HTTP ${response.status}${infProxyLabel}\\n`);\n return 1;\n }\n\n const payload = await response.json();\n const assistantText = Array.isArray(payload?.content)\n ? payload.content\n .filter((item) => item && item.type === \"text\" && typeof item.text === \"string\")\n .map((item) => item.text)\n .join(\"\\n\")\n .trim()\n : \"\";\n const visibleAssistantText = sanitizeTestAssistantText(assistantText);\n const summary = await waitForLatestRequestSummary(paths.logsDir, {\n sinceIso: startedAtIso,\n sleepFn: sleep,\n matcher: (entry) => entry?.success === true || String(entry?.finalProvider || \"\").trim() !== \"\" || String(entry?.finalModel || \"\").trim() !== \"\",\n });\n const finalProvider = String(summary?.finalProvider || \"\").trim();\n const finalModel = String(summary?.finalModel || payload?.model || \"\").trim();\n\n if (!visibleAssistantText && !summary?.success && !finalProvider && !finalModel) {\n stdout.write(\"inference: fail risposta vuota\\n\");\n return 1;\n }\n\n const elapsedMs = Date.now() - startedAtMs;\n stdout.write(`inference: ok${finalProvider || finalModel ? ` (${[finalProvider, finalModel].filter(Boolean).join(\" | \")})` : \"\"}${infProxyLabel} [${elapsedMs}ms]\\n`);\n if (allProviders) {\n const remainingFallbackProviders = buildRemainingFallbackProviders(effectiveProviders, finalProvider, finalModel);\n const workingFallbacks = [];\n const brokenFallbacks = [];\n for (const provider of remainingFallbackProviders) {\n try {\n const probe = await probeProviderForTest({\n fetchFn,\n proxyBaseUrl,\n provider,\n sleepFn: sleep,\n logsDir: paths.logsDir,\n });\n if (probe.ok) {\n workingFallbacks.push({\n id: provider.id,\n model: normalizeInferenceDisplayModel(probe.displayModel || provider.effective_model || provider.default_model || \"\"),\n degraded: probe.degradedStatus || null,\n });\n } else {\n brokenFallbacks.push({\n id: provider.id,\n reason: probe.kind === \"http\"\n ? `HTTP ${probe.status}`\n : probe.message,\n model: probe.displayModel || provider.effective_model || provider.default_model || \"\",\n });\n }\n } catch (error) {\n brokenFallbacks.push({\n id: provider.id,\n reason: error.message || String(error || \"\"),\n model: provider.effective_model || provider.default_model || \"\",\n });\n }\n }\n workingFallbacks.forEach((provider, index) => {\n const degradedLabel = provider.degraded ? ` degraded HTTP ${provider.degraded}` : \"\";\n stdout.write(`${formatOrdinalLabel(index + 1)} fallback: ${provider.id}${degradedLabel} | ${provider.model || \"missing\"}\\n`);\n });\n brokenFallbacks.forEach((provider) => {\n const model = provider.model ? ` (${provider.model})` : \"\";\n stdout.write(`invalid fallback: ${provider.id} -> ${provider.reason}${model}\\n`);\n });\n if (brokenFallbacks.length > 0) {\n if (visibleAssistantText) stdout.write(`response: ${visibleAssistantText}\\n`);\n return 1;\n }\n }\n if (visibleAssistantText) stdout.write(`response: ${visibleAssistantText}\\n`);\n return 0;\n } catch (error) {\n const errorMessage = error.message || String(error || \"\");\n stdout.write(`inference: fail ${errorMessage}\\n`);\n return 1;\n }\n }\n\n for (const provider of selectedTargets) {\n try {\n const probe = await probeProviderForTest({\n fetchFn,\n proxyBaseUrl,\n provider,\n sleepFn: sleep,\n logsDir: paths.logsDir,\n });\n if (!probe.ok) {\n const model = probe.displayModel || probe.model || provider.default_model || \"\";\n if (probe.kind === \"missing-model\") {\n stdout.write(`${provider.id}: skipped (${probe.message})\\n`);\n } else if (probe.kind === \"http\") {\n stdout.write(`${provider.id}: fail ${probe.message} (${model})\\n`);\n } else {\n stdout.write(`${provider.id}: fail ${probe.message} (${model})\\n`);\n }\n failures += 1;\n continue;\n }\n const degradedLabel = probe.degradedStatus ? ` degraded HTTP ${probe.degradedStatus}` : \"\";\n stdout.write(`${provider.id}: ok${degradedLabel} (${probe.displayModel || probe.model || provider.default_model || \"\"})\\n`);\n } catch (error) {\n const model = provider.default_model || (String(provider.provider || \"copilot\") === \"copilot\" ? DEFAULT_COPILOT_MODEL : \"\");\n stdout.write(`${provider.id}: fail ${error.message || String(error || \"\")} (${model})\\n`);\n failures += 1;\n }\n }\n\n return failures === 0 ? 0 : 1;\n }\n\n if (parsed.command === \"claude:setup\") {\n try {\n const models = await resolveAvailableCopilotModels({\n tokenStore,\n fetchFn,\n catalogStore: modelCatalogStore,\n fallbackModels: getAvailableModels(),\n });\n return configureClaudeSettings({ cwd: options.cwd, env, stdout, model: parsed.flags.model, availableModels: models, dataRoot: paths.dataRoot });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"pi:setup\") {\n try {\n return writePiSettings({ cwd: options.cwd, env, stdout, dataRoot: paths.dataRoot });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"vscode-chat:setup\") {\n try {\n const models = resolveModelsFromTokenStore(tokenStore);\n return writeVscodeChatSettings({\n env,\n stdout,\n dataRoot: paths.dataRoot,\n availableModels: models,\n tokenStore,\n });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"vscode-claude:setup\") {\n try {\n const models = await resolveAvailableCopilotModels({\n tokenStore,\n fetchFn,\n catalogStore: modelCatalogStore,\n fallbackModels: getAvailableModels(),\n });\n return writeVscodeClaudeSettings({\n cwd: options.cwd,\n env,\n stdout,\n model: parsed.flags.model,\n availableModels: models,\n dataRoot: paths.dataRoot,\n });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"model:set\") {\n try {\n return setClaudeModel({ cwd: options.cwd, env, stdout, model: parsed.args[0] });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"config:list\") {\n try {\n const scope = resolveConfigListScope(parsed, options.cwd, { ...process.env, ...env });\n const entries = listScopeValues({\n scope,\n cwd: options.cwd,\n serviceConfigFile: paths.serviceConfigFile,\n env: { ...process.env, ...env },\n packageRoot: paths.packageRoot,\n dataRoot: paths.dataRoot,\n });\n stdout.write(`${formatConfigList(entries, scope)}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"config:migrate\") {\n try {\n const migrated = migrateManagedConfig({\n cwd: options.cwd,\n serviceConfigFile: paths.serviceConfigFile,\n env,\n packageRoot: paths.packageRoot,\n dataRoot: paths.dataRoot,\n });\n const changedScopes = [\n migrated.project ? \"project\" : \"\",\n migrated.global ? \"global\" : \"\",\n migrated.service ? \"service\" : \"\",\n ].filter(Boolean);\n stdout.write(changedScopes.length > 0\n ? `Config migration completed: ${changedScopes.join(\", \")}\\n`\n : \"Config migration completed: no changes\\n\");\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"config:get\") {\n try {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key) {\n throw new Error(\"Uso: llmproxy config:get [--project|--service|--scope ]\");\n }\n const entry = getScopeValue({\n key,\n scope: resolveConfigScopeFromParsed(parsed, key),\n cwd: options.cwd,\n serviceConfigFile: paths.serviceConfigFile,\n env: { ...process.env, ...env },\n packageRoot: paths.packageRoot,\n dataRoot: paths.dataRoot,\n });\n stdout.write(`${entry.scope}.${entry.key}=${entry.value == null ? \"\" : String(entry.value)}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"config:set\") {\n try {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key || parsed.args.length < 2) {\n throw new Error(\"Uso: llmproxy config:set [--project|--service|--scope ]\");\n }\n const scope = resolveConfigScopeFromParsed(parsed, key);\n if (scope === \"service\" && key === \"LLMPROXY_MODE\") {\n await validatePlatformModeSwitch({\n fetchFn,\n env,\n serviceConfigFile: paths.serviceConfigFile,\n nextValue: parsed.args[1],\n });\n }\n const entry = setScopeValue({\n key,\n value: parsed.args[1],\n scope,\n cwd: options.cwd,\n serviceConfigFile: paths.serviceConfigFile,\n env: { ...process.env, ...env },\n });\n stdout.write(`Configurazione aggiornata: ${entry.scope}.${entry.key}=${entry.value}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"config:unset\") {\n try {\n const key = String(parsed.args[0] || \"\").trim();\n if (!key) {\n throw new Error(\"Uso: llmproxy config:unset [--project|--service|--scope ]\");\n }\n const entry = unsetScopeValue({\n key,\n scope: resolveConfigScopeFromParsed(parsed, key),\n cwd: options.cwd,\n serviceConfigFile: paths.serviceConfigFile,\n env: { ...process.env, ...env },\n });\n stdout.write(`Configurazione rimossa: ${entry.scope}.${entry.key}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"update\") {\n const locale = resolveOutputLocale({ env, paths });\n const currentVersion = readPackageVersion(paths.packageRoot).trim();\n try {\n const remoteVersion = await readRemotePackageVersion(fetchFn, undefined, {\n timeoutMs: options.remoteVersionTimeoutMs,\n });\n const versionComparison = remoteVersion ? compareReleaseVersions(currentVersion, remoteVersion) : null;\n if (remoteVersion && (remoteVersion === currentVersion || (versionComparison != null && versionComparison >= 0))) {\n if (locale === \"en\") {\n stdout.write(`Already up to date. Current version: ${currentVersion}\\n`);\n } else {\n stdout.write(`Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n }\n return 0;\n }\n } catch {\n // If the remote version check fails, continue with the standard update flow.\n }\n const preflight = resolveUpdatePrerequisites({\n commandRunner: options.commandRunner || spawnSync,\n env,\n paths,\n targetPlatform,\n });\n if (!preflight.ok) {\n stderr.write(formatUpdatePrerequisitesFailure(preflight, locale));\n return 1;\n }\n const result = targetPlatform === \"win32\"\n ? runSelfUpdateWindows(options.commandRunner, undefined, locale)\n : runSelfUpdate(options.commandRunner, undefined, locale);\n if (result?.status === 0) {\n const rawStdout = String(result.stdout || \"\");\n const versionMatch = rawStdout.match(/^__LLMPROXY_VERSION__=(.+)$/m);\n const releaseNotesMatch = rawStdout.match(/__LLMPROXY_RELEASE_NOTES_START__\\n([\\s\\S]*?)\\n__LLMPROXY_RELEASE_NOTES_END__/m);\n const serviceRestartWarningMatch = rawStdout.match(/^__LLMPROXY_SERVICE_RESTART_WARNING__=(.+)$/m);\n const rollbackMatch = rawStdout.match(/^__LLMPROXY_ROLLBACK__=(.+)$/m);\n const rollbackVersionMatch = rawStdout.match(/^__LLMPROXY_ROLLBACK_VERSION__=(.+)$/m);\n const rollbackReasonMatch = rawStdout.match(/^__LLMPROXY_ROLLBACK_REASON__=(.+)$/m);\n const visibleStdout = rawStdout\n .replace(/^__LLMPROXY_VERSION__=.+$/m, \"\")\n .replace(/__LLMPROXY_RELEASE_NOTES_START__\\n[\\s\\S]*?\\n__LLMPROXY_RELEASE_NOTES_END__\\n?/m, \"\")\n .replace(/^__LLMPROXY_SERVICE_RESTART_WARNING__=.+$/m, \"\")\n .replace(/^__LLMPROXY_ROLLBACK__=.+$/m, \"\")\n .replace(/^__LLMPROXY_ROLLBACK_VERSION__=.+$/m, \"\")\n .replace(/^__LLMPROXY_ROLLBACK_REASON__=.+$/m, \"\")\n .trim();\n if (rollbackMatch?.[1] && rollbackMatch[1].trim() !== \"0\") {\n if (visibleStdout) stdout.write(`${visibleStdout}\\n`);\n const restoredVersion = String(rollbackVersionMatch?.[1] || \"\").trim();\n const rollbackReason = String(rollbackReasonMatch?.[1] || \"\").trim();\n const prefix = locale === \"en\"\n ? \"Update failed after installation. The previous version has been restored.\"\n : \"Aggiornamento fallito dopo l'installazione. La versione precedente e' stata ripristinata.\";\n stderr.write(`${prefix}\\n`);\n if (rollbackReason) {\n stderr.write(`${locale === \"en\" ? \"Rollback reason\" : \"Motivo rollback\"}: ${rollbackReason}\\n`);\n }\n if (restoredVersion) {\n stderr.write(`${locale === \"en\" ? \"Restored version\" : \"Versione ripristinata\"}: ${restoredVersion}\\n`);\n }\n return 1;\n }\n stdout.write(\"Aggiornamento completato.\\n\");\n if (visibleStdout) stdout.write(`${visibleStdout}\\n`);\n if (versionMatch?.[1]) {\n const currentVersion = String(versionMatch[1]).trim();\n stdout.write(`Versione corrente: ${currentVersion}\\n`);\n const releaseNotes = releaseNotesMatch?.[1]?.trim() || formatReleaseNotes(currentVersion, locale, { packageRoot: paths.packageRoot });\n stdout.write(`${releaseNotes}\\n`);\n if (serviceRestartWarningMatch?.[1]) {\n stdout.write(`Warning riavvio servizio: ${String(serviceRestartWarningMatch[1]).trim()}\\n`);\n }\n return 0;\n }\n stderr.write(\"Aggiornamento completato, ma il rilancio di llmproxy e' fallito.\\n\");\n return 1;\n }\n stderr.write((result?.stderr && String(result.stderr).trim()) || \"Aggiornamento fallito.\\n\");\n return 1;\n }\n\n if (parsed.command === \"uninstall\") {\n const commandRunner = options.commandRunner || spawnSync;\n\n serviceManager.stop();\n\n const serviceFile = targetPlatform === \"darwin\"\n ? paths.launchAgentFile\n : targetPlatform === \"win32\"\n ? paths.serviceRunnerFile\n : paths.systemdUnitFile;\n try { fs.unlinkSync(serviceFile); } catch { /* ignore if missing */ }\n\n // On Windows, also delete the Windows service via sc.exe\n if (targetPlatform === \"win32\") {\n try {\n spawnSync(\"sc.exe\", [\"delete\", \"llmproxy\"], { encoding: \"utf8\", shell: true });\n } catch { /* ignore */ }\n }\n\n if (isDockerManagedRuntime(env, paths, targetPlatform)) {\n const composeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(paths.packageRoot, \"docker-compose.production.yml\")).trim();\n const composeEnv = resolveServiceEnvironment({ env, paths, dockerComposeFile: composeFile });\n const spawnOptions = { env: { ...process.env, ...composeEnv } };\n runDockerCompose(commandRunner, composeFile, [\"down\", \"--remove-orphans\"], { spawnOptions });\n }\n\n try { fs.rmSync(paths.dataRoot, { recursive: true, force: true }); } catch { /* ignore */ }\n\n const result = targetPlatform === \"win32\"\n ? runSelfUninstallWindows(commandRunner)\n : runSelfUninstall(commandRunner);\n if (result?.status === 0) {\n stdout.write(\"Disinstallazione completata.\\n\");\n return 0;\n }\n stderr.write((result?.stderr && String(result.stderr).trim()) || \"Disinstallazione fallita.\\n\");\n return 1;\n }\n\n if (parsed.command === \"login\") {\n stdout.write(\"Warning: `llmproxy login` e' un alias legacy. Usa `llmproxy provider:add copilot`.\\n\");\n const deviceData = await startDeviceFlow({ fetchFn });\n stdout.write(`Apri ${deviceData.verification_uri} e inserisci il codice ${deviceData.user_code}\\n`);\n const result = await pollForToken(deviceData.device_code, deviceData.interval || 5, { fetchFn, store: tokenStore, sleep });\n if (!result.success) {\n stderr.write(`Login fallito: ${result.error}\\n`);\n return 1;\n }\n stdout.write(\"Login completato.\\n\");\n return 0;\n }\n\n if (parsed.command === \"provider:add\") {\n const rawProviderArg = String(parsed.args[0] || \"\").trim();\n const providerId = normalizeKnownProviderId(rawProviderArg);\n const apiKey = String(parsed.flags[\"api-key\"] || \"\").trim();\n const apiKeyProviderKind = resolveApiKeyProviderKind(rawProviderArg, parsed.flags.provider);\n const providerKind = apiKey ? apiKeyProviderKind : providerId;\n const instanceId = apiKey && providerId && providerId !== providerKind ? providerId : providerKind;\n const providerName = String(parsed.flags.name || getKnownProvider(providerKind || providerId).displayName || instanceId || providerKind || providerId || \"\").trim();\n const defaultModel = String(parsed.flags.model || parsed.flags[\"default-model\"] || \"\").trim();\n const explicitQwenPlan = normalizeQwenEndpointVariant(parsed.flags.plan);\n const endpointVariant = resolveQwenEndpointVariant(providerKind || providerId, parsed.flags.plan, apiKey);\n const visionFlag = parsed.flags.vision;\n const freeModelFlag = parsed.flags[\"free-model\"];\n const visionEnabled = visionFlag === \"true\" || visionFlag === true;\n const visionDisabled = visionFlag === \"false\" || visionFlag === false;\n // --proxy senza valore = rotazione sui proxy registrati\n // --proxy = proxy specifico (comportamento esistente)\n const proxyBareFlag = parsed.flags.proxy === true;\n const proxyUrlRaw = proxyBareFlag ? \"\" : String(parsed.flags.proxy || \"\").trim();\n const proxyApiKey = String(parsed.flags[\"proxy-key\"] || \"\").trim();\n if (!providerId) {\n stderr.write(\"Provider id richiesto. Uso: llmproxy provider:add --api-key --model --vision [--name ] [--plan ] [--proxy ] [--proxy-key ]\\n\");\n return 1;\n }\n\n const providerInfo = getKnownProvider(providerKind || providerId);\n if (apiKey && !apiKeyProviderKind) {\n stderr.write(`Provider API-key non supportato: ${providerId}. Usa un id canonico oppure --provider , ad esempio: llmproxy provider:add opencode-bacin --provider opencode --api-key --model --vision false\\n`);\n return 1;\n }\n if (!providerInfo.auth) {\n stderr.write(`Provider non supportato: ${providerId}. Esempi: copilot, openrouter, kimi, qwen, z.ai, opencode, opencode-go\\n`);\n return 1;\n }\n\n if (providerInfo.auth === \"api_key\") {\n if (!apiKey) {\n stderr.write(`Il provider ${providerId} richiede --api-key. Uso: llmproxy provider:add ${providerId} --api-key --model --vision [--proxy ] [--proxy-key ]\\n`);\n return 1;\n }\n if (providerKind === \"qwen\" && parsed.flags.plan && !explicitQwenPlan) {\n stderr.write(\"Per `qwen` usa --plan subscription oppure --plan payg.\\n\");\n return 1;\n }\n if (!defaultModel) {\n stderr.write(`Il provider ${providerKind} richiede --model per salvare e verificare il modello di default.\\n`);\n return 1;\n }\n if (visionFlag === undefined || visionFlag === null || visionFlag === \"\") {\n stderr.write(`Il provider ${providerKind} richiede --vision . Esempio: llmproxy provider:add ${instanceId || providerKind} --api-key --model ${defaultModel} --vision true\\n`);\n return 1;\n }\n if (!visionEnabled && !visionDisabled) {\n stderr.write(`--vision deve essere 'true' oppure 'false'. Valore ricevuto: '${visionFlag}'\\n`);\n return 1;\n }\n const freeModel = parseOptionalBooleanFlag(freeModelFlag, { defaultValue: false, allowBareTrue: true });\n if (freeModel === null) {\n stderr.write(`--free-model deve essere 'true' oppure 'false'. Valore ricevuto: '${freeModelFlag}'\\n`);\n return 1;\n }\n const proxyUrl = (proxyUrlRaw || proxyApiKey) ? buildProxyAgentUrl(proxyUrlRaw, proxyApiKey) : \"\";\n const probe = await probeApiKeyProviderModel({ provider: { provider: providerKind, endpoint_variant: endpointVariant }, apiKey, model: defaultModel, fetchFn, proxyUrl });\n if (!probe.ok) {\n stderr.write(`Test provider fallito per ${providerKind}/${defaultModel}: ${probe.status || \"network\"} ${probe.error || \"\"}\\n`);\n return 1;\n }\n const allProviders = providerStore.listProviders();\n const sameKindProviders = allProviders.filter((p) => String(p.provider || \"\").toLowerCase() === providerKind);\n const hasExplicitInstanceId = Boolean(providerId && providerId !== providerKind);\n const hasExplicitName = Boolean(providerName && providerName !== (getKnownProvider(providerKind || providerId)?.displayName || \"\"));\n const nameBasedId = hasExplicitName && !hasExplicitInstanceId\n ? providerName.toLowerCase().replace(/[^a-z0-9_-]/g, \"-\").replace(/^-+|-+$/g, \"\")\n : null;\n const existingWithSameModel = (hasExplicitInstanceId || nameBasedId) ? null : sameKindProviders.find((p) => p.default_model === defaultModel);\n const targetInstanceId = existingWithSameModel\n ? existingWithSameModel.id\n : (hasExplicitInstanceId ? providerId : (nameBasedId || (sameKindProviders.length > 0 ? defaultModel : providerKind)));\n\n providerStore.saveProvider(targetInstanceId, {\n access_token: apiKey,\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: providerKind,\n auth_type: \"api_key\",\n default_model: defaultModel,\n endpoint_variant: endpointVariant,\n vision: visionEnabled,\n free_model: freeModel,\n proxy_url: proxyUrl || undefined,\n proxy_api_key: proxyApiKey || undefined,\n proxy_rotation: proxyBareFlag ? true : undefined,\n }, { name: providerName || providerId });\n // Fetch and store coding score from live API (once at provider insertion)\n fetchCodingScore(defaultModel, fetchFn).then((score) => {\n if (typeof score === \"number\" && Number.isFinite(score)) {\n providerStore.saveProvider(targetInstanceId, { coding_score: score }, { name: providerName || providerId });\n }\n return null;\n }).catch(() => {});\n const planSuffix = providerKind === \"qwen\" ? `, plan: ${formatQwenPlanLabel(endpointVariant)}` : \"\";\n stdout.write(`Provider configurato con API key: ${targetInstanceId} (default model: ${defaultModel}, vision: ${visionEnabled}, free: ${freeModel}${planSuffix}).\\n`);\n return 0;\n }\n\n const deviceData = await startDeviceFlow({ fetchFn });\n stdout.write(`Apri ${deviceData.verification_uri} e inserisci il codice ${deviceData.user_code}\\n`);\n const result = await pollForToken(deviceData.device_code, deviceData.interval || 5, {\n fetchFn,\n sleep,\n store: {\n save(data) {\n return providerStore.saveProvider(providerId, {\n ...data,\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: defaultModel || DEFAULT_COPILOT_MODEL,\n }, { name: providerName || providerId });\n },\n },\n });\n if (!result.success) {\n stderr.write(`Login fallito: ${result.error}\\n`);\n return 1;\n }\n // Fetch and store coding score after successful login\n const copilotModel = defaultModel || DEFAULT_COPILOT_MODEL;\n fetchCodingScore(copilotModel, fetchFn).then((score) => {\n if (typeof score === \"number\" && Number.isFinite(score)) {\n providerStore.saveProvider(providerId, { coding_score: score }, { name: providerName || providerId });\n }\n return null;\n }).catch(() => {});\n stdout.write(`Login completato per provider ${providerId}.\\n`);\n return 0;\n }\n\n if (parsed.command === \"provider:key\") {\n const providerId = normalizeKnownProviderId(parsed.args[0]);\n const apiKey = String(parsed.flags[\"api-key\"] || \"\").trim();\n const defaultModel = String(parsed.flags.model || parsed.flags[\"default-model\"] || \"\").trim();\n const explicitQwenPlan = normalizeQwenEndpointVariant(parsed.flags.plan);\n const visionFlag = parsed.flags.vision;\n const freeModelFlag = parsed.flags[\"free-model\"];\n if (!providerId || !apiKey) {\n stderr.write(\"Uso: llmproxy provider:key --api-key [--model ] [--vision ] [--free-model ] [--plan ]\\n\");\n return 1;\n }\n\n const providerInfo = getKnownProvider(providerId);\n if (!providerInfo.auth) {\n stderr.write(`Provider non supportato: ${providerId}.\\n`);\n return 1;\n }\n if (providerInfo.auth !== \"api_key\") {\n stderr.write(`Il provider ${providerId} non usa API key. Usa \\`llmproxy provider:add ${providerId}\\` per autenticazione OAuth.\\n`);\n return 1;\n }\n\n let existing = providerStore.getProvider(providerId);\n if (!existing && defaultModel) {\n const allProviders = providerStore.listProviders();\n existing = allProviders.find((p) => String(p.provider || \"\").toLowerCase() === providerId && p.default_model === defaultModel) || null;\n }\n const effectiveId = existing ? existing.id : providerId;\n const endpointVariant = resolveQwenEndpointVariant(providerId, parsed.flags.plan, apiKey) || existing?.endpoint_variant || \"\";\n const nextDefaultModel = defaultModel || existing?.default_model || \"\";\n if (!nextDefaultModel) {\n stderr.write(`Il provider ${providerId} non ha un modello di default salvato. Usa --model .\\n`);\n return 1;\n }\n if (providerId === \"qwen\" && parsed.flags.plan && !explicitQwenPlan) {\n stderr.write(\"Per `qwen` usa --plan subscription oppure --plan payg.\\n\");\n return 1;\n }\n let nextVision = existing?.vision;\n if (visionFlag !== undefined && visionFlag !== null && visionFlag !== \"\") {\n if (visionFlag !== \"true\" && visionFlag !== true && visionFlag !== \"false\" && visionFlag !== false) {\n stderr.write(`--vision deve essere 'true' oppure 'false'. Valore ricevuto: '${visionFlag}'\\n`);\n return 1;\n }\n nextVision = visionFlag === \"true\" || visionFlag === true;\n }\n let nextFreeModel = existing?.free_model;\n if (freeModelFlag !== undefined && freeModelFlag !== null && freeModelFlag !== \"\") {\n nextFreeModel = parseOptionalBooleanFlag(freeModelFlag, { defaultValue: null, allowBareTrue: true });\n if (nextFreeModel === null) {\n stderr.write(`--free-model deve essere 'true' oppure 'false'. Valore ricevuto: '${freeModelFlag}'\\n`);\n return 1;\n }\n }\n const probe = await probeApiKeyProviderModel({ provider: { provider: providerId, endpoint_variant: endpointVariant }, apiKey, model: nextDefaultModel, fetchFn });\n if (!probe.ok) {\n stderr.write(`Test provider fallito per ${providerId}/${nextDefaultModel}: ${probe.status || \"network\"} ${probe.error || \"\"}\\n`);\n return 1;\n }\n providerStore.saveProvider(effectiveId, {\n ...(existing || {}),\n access_token: apiKey,\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: providerId,\n auth_type: \"api_key\",\n default_model: nextDefaultModel,\n endpoint_variant: endpointVariant,\n vision: nextVision,\n free_model: nextFreeModel,\n }, { name: existing?.name || providerInfo.displayName || providerId });\n // Fetch and store coding score (model scores are static — fetch once)\n fetchCodingScore(nextDefaultModel, fetchFn).then((score) => {\n if (typeof score === \"number\" && Number.isFinite(score)) {\n providerStore.saveProvider(effectiveId, { coding_score: score }, { name: existing?.name || providerInfo.displayName || providerId });\n }\n return null;\n }).catch(() => {});\n\n const planSuffix = providerId === \"qwen\" ? `, plan: ${formatQwenPlanLabel(endpointVariant)}` : \"\";\n const visionSuffix = nextVision !== undefined ? `, vision: ${nextVision}` : \"\";\n const freeModelSuffix = nextFreeModel !== undefined ? `, free: ${nextFreeModel}` : \"\";\n stdout.write(`API key aggiornata per provider ${effectiveId} (default model: ${nextDefaultModel}${visionSuffix}${freeModelSuffix}${planSuffix}).\\n`);\n return 0;\n }\n\n if (parsed.command === \"provider:list\") {\n const providers = providerStore.listProviders();\n if (providers.length === 0) {\n stdout.write(\"Nessun provider configurato.\\n\");\n return 0;\n }\n const projectSettings = resolveClaudeProjectSettings(path.resolve(String(options.cwd || process.cwd())), { env });\n const effectiveProviders = resolveEffectiveProviderList(providers, projectSettings.configuredModel);\n const creditCache = new Map();\n const priceCache = new Map();\n const codingCache = new Map();\n const reorderingCache = providerStore.__llmproxyProviderReordering\n ? providerStore.__llmproxyProviderReordering\n : (() => {\n const storePath = path.join(paths.dataRoot, DEFAULT_REORDERING_FILE);\n const store = readReorderingStore(storePath);\n return { getStore: () => store };\n })();\n const reorderingStore = reorderingCache.getStore();\n const probeFn = buildDefaultProbeFn({ proxyStore });\n // Spinner: segnala che il benchmarking dei provider è in corso\n const benchSpinner = (process.stdout.isTTY)\n ? startSpinner(\"We are benchmarking each provider/model for you: please wait\")\n : null;\n const providersWithCredit = await Promise.all(\n effectiveProviders.providers.map(async (provider) => {\n const [coding_info, credit_info, price_info, reorder_info] = await Promise.all([\n fetchProviderCodingInfo(provider, fetchFn, codingCache),\n fetchProviderCreditInfo(provider, fetchFn, creditCache),\n fetchProviderPriceInfo(provider, fetchFn, priceCache),\n Promise.resolve(reorderingStore.scores?.[provider.id] || null),\n ]);\n let benchMetric = reorder_info?.speed != null\n ? `${reorder_info.speed} ms`\n : \"n/a\";\n // Fallback: fetch speed probe live if not in reorder info\n if (benchMetric === \"n/a\") {\n const speedResult = await fetchSpeedMetric(provider, probeFn, fetchFn);\n if (typeof speedResult === \"number\") {\n benchMetric = `${speedResult} ms`;\n } else if (typeof speedResult === \"string\") {\n benchMetric = speedResult; // error message like \"errore 429\"\n } else {\n benchMetric = \"n/a\";\n }\n }\n // Add benchmark_score field for display\n const benchmarkScore = reorder_info?.power != null\n ? reorder_info.power\n : (await fetchBenchmarkMetric(provider, fetchFn));\n return {\n ...provider,\n coding_info,\n credit_info,\n price_info,\n reorder_info,\n bench_metric: benchMetric,\n benchmark_score: benchmarkScore,\n };\n }),\n );\n if (benchSpinner) stopSpinner(benchSpinner);\n if (effectiveProviders.projectOverrideActive) {\n stdout.write(`Provider effettivi per il progetto (override: ${effectiveProviders.configuredModel}):\\n`);\n }\n if (reorderingStore.criteria.length > 0) {\n const ageMinutes = reorderingStore.lastUpdatedMs ? Math.round((Date.now() - reorderingStore.lastUpdatedMs) / 60000) : null;\n stdout.write(`reorder=${reorderingStore.criteria.join(\">\")}${ageMinutes != null ? ` (ultimo: ${ageMinutes}m fa)` : \"\"}\\n`);\n }\n stdout.write(`${formatProviderList(providersWithCredit, { stdout, env })}\\n`);\n return 0;\n }\n\n if (parsed.command === \"provider:test\") {\n const providers = providerStore.listProviders();\n if (providers.length === 0) {\n stdout.write(\"Nessun provider configurato. Usa 'llmproxy provider:add' per aggiungere un provider.\\n\");\n return 1;\n }\n\n const visionMode = parsed.flags[\"vision\"] === true;\n const allProxiesMode = parsed.flags[\"all-proxies\"] === true;\n const forcedProxyUrl = String(parsed.flags.proxy || \"\").trim();\n const forcedProviderId = String(parsed.flags.provider || \"\").trim();\n const proxyInferenceMode = allProxiesMode || Boolean(forcedProxyUrl);\n let passCount = 0;\n let failCount = 0;\n let skipCount = 0;\n let proxyUnreachableCount = 0;\n let proxiesList = [];\n let testImageBase64 = \"\";\n let visionPrompt = \"\";\n let visionKeywords = [];\n if (forcedProxyUrl) {\n stdout.write(`Test di inferenza con proxy forzato: ${forcedProxyUrl}\\n\\n`);\n proxiesList = [{ url: forcedProxyUrl }];\n } else if (allProxiesMode) {\n proxiesList = proxyStore.listProxies();\n if (proxiesList.length === 0) {\n stdout.write(\"Nessun proxy registrato. Usa 'llmproxy proxy:add ' per aggiungere proxy.\\n\");\n return 1;\n }\n stdout.write(\"Test di inferenza provider × proxy...\\n\\n\");\n } else if (visionMode) {\n // Test visione: leggi l'immagine di test dal file assets\n const testImagePath = path.join(paths.packageRoot, \"assets\", \"test-vision.png\");\n try {\n const imageBuffer = fs.readFileSync(testImagePath);\n testImageBase64 = imageBuffer.toString('base64');\n } catch (err) {\n stdout.write(`Errore: impossibile leggere l'immagine di test da ${testImagePath}\\n`);\n return 1;\n }\n\n // Prompt che chiede esplicitamente di descrivere i colori\n visionPrompt = \"Descrivi questa immagine. Quali colori vedi e dove sono posizionati?\";\n\n // Parole chiave che indicano visione effettiva\n visionKeywords = [\"rosso\", \"red\", \"blu\", \"blue\", \"colore\", \"color\", \"immagine\", \"image\", \"alto\", \"top\", \"basso\", \"bottom\", \"sopra\", \"above\", \"sotto\", \"below\"];\n stdout.write(\"Test visione provider...\\n\\n\");\n } else {\n // Default: inferenza testuale semplice\n stdout.write(\"Test inferenza provider...\\n\\n\");\n }\n\n for (const provider of providers) {\n const model = provider.default_model;\n const vision = provider.vision;\n const providerName = provider.name || provider.id;\n\n if (!model) {\n stdout.write(`⏭️ ${providerName}: saltato (modello non configurato)\\n`);\n skipCount++;\n continue;\n }\n\n // Filtra per --provider esplicito\n if (forcedProviderId && provider.id !== forcedProviderId && (provider.name || \"\").toLowerCase() !== forcedProviderId.toLowerCase()) {\n continue;\n }\n\n const proxyUrls = proxyInferenceMode\n ? proxiesList.map((p) => p.url)\n : [\"\"];\n\n if (proxyInferenceMode) {\n stdout.write(`${providerName} (${model})\\n`);\n }\n\n for (const proxyUrl of proxyUrls) {\n const providerStartedAtIso = new Date().toISOString();\n if (proxyInferenceMode) {\n const proxyHost = proxyUrl ? new URL(proxyUrl).hostname : \"direct\";\n stdout.write(` ${proxyHost}\\n`);\n } else if (visionMode) {\n const proxyLabel = proxyUrl ? ` (via ${new URL(proxyUrl).hostname})` : \"\";\n const expectedStatus = vision === true ? \"visione ✅\" : vision === false ? \"testo ❌\" : \"visione sconosciuta ⚠️\";\n stdout.write(`🔍 ${providerName} (${model})${proxyLabel} - atteso: ${expectedStatus}\\n`);\n } else {\n // Default text inference: simple label\n const proxyLabel = proxyUrl ? ` (via ${new URL(proxyUrl).hostname})` : \"\";\n stdout.write(`🔍 ${providerName} (${model})${proxyLabel}\\n`);\n }\n\n try {\n const baseUrl = getProxyBaseUrl({ env, dataRoot: paths.dataRoot });\n const testTokenPrefix = proxyInferenceMode ? \"llmproxy-proxy-test-\" : \"llmproxy-test-\";\n // Determine messages based on mode\n const buildMessages = (providerUnderTest) => visionMode\n ? [\n {\n role: \"user\",\n content: [\n { type: \"text\", text: visionPrompt },\n {\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: \"image/png\",\n data: testImageBase64,\n },\n },\n ],\n },\n ]\n : [\n {\n role: \"user\",\n content: [{ type: \"text\", text: `Rispondi solo: ${testTokenPrefix}${providerUnderTest.id}` }],\n },\n ];\n const response = await withTemporaryProviderProxy(providerStore, provider, proxyUrl, async (providerUnderTest) => (\n runLocalProxyTestRequest(\n fetchFn,\n `${baseUrl}/v1/messages`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n provider: providerUnderTest.id,\n model,\n stream: false,\n max_tokens: 150,\n messages: buildMessages(providerUnderTest),\n }),\n },\n sleep,\n )\n ));\n\n if (!response.ok) {\n const errorText = await response.text();\n if (visionMode && vision === false) {\n stdout.write(` ✅ PASS - Visione correttamente disabilitata (errore HTTP ${response.status})\\n`);\n passCount++;\n continue;\n }\n if (proxyInferenceMode) {\n stdout.write(` FAIL HTTP ${response.status}: ${errorText.replace(/\\s+/g, \" \").slice(0, 180)}\\n`);\n } else {\n stdout.write(` ❌ Errore HTTP ${response.status}: ${errorText.slice(0, 100)}\\n`);\n }\n failCount++;\n continue;\n }\n\n const payload = await response.json();\n const assistantText = extractAssistantTextFromPayload(payload);\n const visibleAssistantText = sanitizeTestAssistantText(assistantText);\n const summary = proxyInferenceMode\n ? await waitForLatestRequestSummary(paths.logsDir, {\n sinceIso: providerStartedAtIso,\n sleepFn: sleep,\n matcher: (entry) => entry?.success === true && requestSummaryMatchesProvider(entry, provider),\n })\n : null;\n\n if (proxyInferenceMode) {\n if (!visibleAssistantText && !summary?.success) {\n stdout.write(\" FAIL risposta vuota\\n\");\n failCount++;\n continue;\n }\n const finalModel = String(summary?.finalModel || payload?.model || model || \"\").trim();\n stdout.write(` OK ${finalModel}\\n`);\n if (visibleAssistantText) {\n stdout.write(` ${visibleAssistantText.replace(/\\s+/g, \" \").slice(0, 160)}\\n`);\n } else {\n stdout.write(\" risposta testuale non visibile, ma inferenza completata correttamente\\n\");\n }\n passCount++;\n continue;\n }\n\n if (visionMode) {\n // Vision mode: check response for vision indicators\n const reasoningText = payload.reasoning_content || \"\";\n const combinedText = [assistantText, reasoningText].filter(t => t).join(\"\\n\");\n\n if (!combinedText) {\n stdout.write(` ❌ Risposta vuota\\n`);\n failCount++;\n continue;\n }\n\n const responseLower = combinedText.toLowerCase();\n const hasVisionIndicators = visionKeywords.some((keyword) => responseLower.includes(keyword));\n\n if (hasVisionIndicators) {\n if (vision === true) {\n stdout.write(` ✅ PASS - Visione confermata\\n`);\n stdout.write(` Risposta: ${combinedText.slice(0, 100)}...\\n`);\n passCount++;\n } else {\n stdout.write(` ⚠️ WARN - Visione rilevata ma flag vision=false\\n`);\n stdout.write(` Risposta: ${combinedText.slice(0, 100)}...\\n`);\n passCount++;\n }\n } else {\n if (vision === false) {\n stdout.write(` ✅ PASS - Visione correttamente disabilitata\\n`);\n stdout.write(` Risposta: ${combinedText.slice(0, 80)}...\\n`);\n passCount++;\n } else {\n stdout.write(` ❌ FAIL - Visione attesa ma non rilevata\\n`);\n stdout.write(` Risposta: ${combinedText.slice(0, 80)}...\\n`);\n failCount++;\n }\n }\n continue;\n }\n\n // Default text inference mode\n if (!visibleAssistantText) {\n stdout.write(` ❌ Risposta vuota\\n`);\n failCount++;\n continue;\n }\n const finalModel = String(payload?.model || model || \"\").trim();\n stdout.write(` ✅ PASS (${finalModel})\\n`);\n stdout.write(` ${visibleAssistantText.replace(/\\s+/g, \" \").slice(0, 160)}\\n`);\n passCount++;\n } catch (error) {\n const isProxyError = String(error.message || \"\").includes(\"fetch failed\");\n if (isProxyError && allProxiesMode) {\n stdout.write(proxyInferenceMode\n ? \" Proxy non raggiungibile\\n\"\n : \" ℹ️ Proxy non raggiungibile (ignorato in modalità probe)\\n\");\n proxyUnreachableCount++;\n } else {\n stdout.write(proxyInferenceMode\n ? ` FAIL ${error.message}\\n`\n : ` ❌ Errore: ${error.message}\\n`);\n failCount++;\n }\n }\n } // fine loop proxyUrl\n\n if (proxyInferenceMode) {\n stdout.write(\"\\n\");\n }\n }\n\n stdout.write(`Risultati: ${passCount} pass, ${failCount} fail, ${skipCount} skip`);\n if (proxyUnreachableCount > 0) {\n stdout.write(`, ${proxyUnreachableCount} proxy non raggiungibili (non contati come fallimento)`);\n }\n stdout.write(\"\\n\");\n\n return failCount === 0 ? 0 : 1;\n }\n\n if (parsed.command === \"provider:available\") {\n stdout.write(`${formatAvailableProviders()}\\n`);\n return 0;\n }\n\n if (parsed.command === \"provider:status\") {\n const providers = providerStore.listProviders();\n if (providers.length === 0) {\n stdout.write(\"Nessun provider configurato.\\n\");\n return 0;\n }\n stdout.write(`${formatProviderStatus(providers)}\\n`);\n return 0;\n }\n\n if (parsed.command === \"provider:usage\") {\n try {\n const meteringSink = createJsonlMeteringSink({ filePath: paths.meteringFile });\n const allRecords = meteringSink.query({ filters: {}, limit: 1000, offset: 0, order: \"desc\" }).records;\n\n const now = new Date();\n const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString();\n const weekStart = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString();\n const monthStart = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString();\n\n function filterByPeriod(records, from) {\n return records.filter((r) => r.timestamp >= from);\n }\n\n function computePeriodStats(records) {\n const byProvider = {};\n const byModel = {};\n let totalInput = 0;\n let totalOutput = 0;\n let totalRequests = 0;\n\n for (const r of records) {\n if (r.success === false) continue;\n totalRequests++;\n const input = Number(r.tokens_input || r.prompt_tokens || 0);\n const output = Number(r.tokens_output || r.completion_tokens || 0);\n totalInput += input;\n totalOutput += output;\n\n const provider = r.provider || \"unknown\";\n if (!byProvider[provider]) byProvider[provider] = { requests: 0, input: 0, output: 0 };\n byProvider[provider].requests++;\n byProvider[provider].input += input;\n byProvider[provider].output += output;\n\n const model = r.model_used || \"unknown\";\n if (!byModel[model]) byModel[model] = { requests: 0, input: 0, output: 0 };\n byModel[model].requests++;\n byModel[model].input += input;\n byModel[model].output += output;\n }\n\n return { totalRequests, totalInput, totalOutput, byProvider, byModel };\n }\n\n const todayRecords = filterByPeriod(allRecords, todayStart);\n const weekRecords = filterByPeriod(allRecords, weekStart);\n const monthRecords = filterByPeriod(allRecords, monthStart);\n\n const todayStats = computePeriodStats(todayRecords);\n const weekStats = computePeriodStats(weekRecords);\n const monthStats = computePeriodStats(monthRecords);\n\n function formatPeriodStats(label, stats) {\n const lines = [\n `\\n${label}:`,\n ` Requests: ${stats.totalRequests} | Tokens: ${stats.totalInput + stats.totalOutput} (in: ${stats.totalInput}, out: ${stats.totalOutput})`,\n ];\n\n const providers = Object.entries(stats.byProvider).sort((a, b) => b[1].requests - a[1].requests);\n if (providers.length > 0) {\n lines.push(` Per provider:`);\n for (const [provider, data] of providers) {\n lines.push(` - ${provider}: ${data.requests} req, ${data.input + data.output} tokens (in: ${data.input}, out: ${data.output})`);\n }\n }\n\n const models = Object.entries(stats.byModel).sort((a, b) => b[1].requests - a[1].requests);\n if (models.length > 0) {\n lines.push(` Per modello:`);\n for (const [model, data] of models.slice(0, 10)) {\n lines.push(` - ${model}: ${data.requests} req, ${data.input + data.output} tokens`);\n }\n if (models.length > 10) {\n lines.push(` ... e altri ${models.length - 10} modelli`);\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n stdout.write(\"Consumo token per periodo:\\n\");\n stdout.write(formatPeriodStats(\"Oggi (dalle 00:00)\", todayStats));\n stdout.write(formatPeriodStats(\"Ultimi 7 giorni\", weekStats));\n stdout.write(formatPeriodStats(\"Ultimi 30 giorni\", monthStats));\n stdout.write(\"\\n\");\n\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"provider:order\") {\n const providerId = String(parsed.args[0] || \"\").trim();\n const position = Number(parsed.args[1] || 0);\n if (!providerId || !Number.isFinite(position) || position <= 0) {\n stderr.write(\"Uso: llmproxy provider:order \\n\");\n return 1;\n }\n try {\n const providers = providerStore.moveProvider(providerId, position);\n stdout.write(`Nuovo ordine provider: ${providers.map((provider) => provider.id).join(\", \")}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"provider:reorder\") {\n const reordering = createProviderReordering({\n tokenStore: providerStore,\n filePath: path.join(paths.dataRoot, DEFAULT_REORDERING_FILE),\n fetchFn,\n probeFn: buildDefaultProbeFn(),\n });\n const result = await reordering.runCycle(env);\n if (!result) {\n stdout.write(\"LLMPROXY_REORDERING non configurata (o vuota): nessun reordering automatico da eseguire.\\n\");\n return 0;\n }\n stdout.write(`Criteri: ${result.criteria.join(\">\")}\\n`);\n stdout.write(`Nuovo ordine provider: ${result.order.join(\", \")}\\n`);\n for (const providerId of result.order) {\n const score = result.scores[providerId] || {};\n const parts = Object.entries(score).map(([key, value]) => `${key}=${value == null ? \"n/a\" : value}`);\n stdout.write(` ${providerId}: ${parts.join(\" \")}\\n`);\n }\n return 0;\n }\n\n if (parsed.command === \"provider:rename\") {\n const providerId = String(parsed.args[0] || \"\").trim();\n const nextName = parsed.args.slice(1).join(\" \").trim();\n if (!providerId || !nextName) {\n stderr.write(\"Uso: llmproxy provider:rename \\n\");\n return 1;\n }\n try {\n const provider = providerStore.renameProvider(providerId, nextName);\n stdout.write(`Provider rinominato: ${provider.id} -> ${provider.name}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"provider:update\") {\n const providerId = String(parsed.args[0] || \"\").trim();\n if (!providerId) {\n stderr.write(\"Uso: llmproxy provider:update [--vision ] [--free-model ] [--name ]\\n\");\n return 1;\n }\n const patch = {};\n const visionRaw = parsed.flags.vision;\n if (visionRaw !== undefined && visionRaw !== null && visionRaw !== \"\") {\n if (visionRaw !== \"true\" && visionRaw !== true && visionRaw !== \"false\" && visionRaw !== false) {\n stderr.write(`--vision deve essere 'true' oppure 'false'. Valore ricevuto: '${visionRaw}'\\n`);\n return 1;\n }\n patch.vision = visionRaw === \"true\" || visionRaw === true;\n }\n const freeRaw = parsed.flags[\"free-model\"];\n if (freeRaw !== undefined && freeRaw !== null && freeRaw !== \"\") {\n const parsedFree = parseOptionalBooleanFlag(freeRaw, { defaultValue: null, allowBareTrue: true });\n if (parsedFree === null) {\n stderr.write(`--free-model deve essere 'true' oppure 'false'. Valore ricevuto: '${freeRaw}'\\n`);\n return 1;\n }\n patch.free_model = parsedFree;\n }\n const nameRaw = parsed.flags.name;\n if (nameRaw !== undefined && nameRaw !== null && nameRaw !== \"\") {\n patch.name = String(nameRaw).trim();\n }\n if (Object.keys(patch).length === 0) {\n stderr.write(\"Nessun campo da aggiornare. Specifica almeno uno tra --vision, --free-model, --name.\\n\");\n return 1;\n }\n try {\n const provider = providerStore.updateProvider(providerId, patch);\n stdout.write(`Provider aggiornato: ${provider.id}\\n`);\n if (patch.vision !== undefined) stdout.write(` vision: ${provider.vision}\\n`);\n if (patch.free_model !== undefined) stdout.write(` free: ${provider.free_model}\\n`);\n if (patch.name) stdout.write(` name: ${provider.name}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"provider:remove\") {\n const providerId = String(parsed.args[0] || \"\").trim();\n if (!providerId) {\n stderr.write(\"Uso: llmproxy provider:remove \\n\");\n return 1;\n }\n providerStore.clearProvider(providerId);\n stdout.write(`Provider rimosso: ${providerId}\\n`);\n return 0;\n }\n\n if (parsed.command === \"proxy:add\") {\n const url = String(parsed.args[0] || \"\").trim();\n const proxyName = String(parsed.flags.name || \"\").trim();\n if (!url) {\n stderr.write(\"URL proxy richiesto. Uso: llmproxy proxy:add [--name ]\\n\");\n return 1;\n }\n try {\n const proxy = proxyStore.addProxy(url, proxyName || undefined);\n const nameSuffix = proxy.name ? ` (${proxy.name})` : \"\";\n stdout.write(`Proxy aggiunto: ${proxy.id}${nameSuffix} (${proxy.url})\\n`);\n return 0;\n } catch (err) {\n stderr.write(`Errore: ${err.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"proxy:list\") {\n const proxies = proxyStore.listProxies();\n if (proxies.length === 0) {\n stdout.write(\"Nessun proxy registrato.\\n\");\n return 0;\n }\n stdout.write(\"Proxy registrati:\\n\");\n proxies.forEach((p, i) => {\n const nameTag = p.name ? ` [${p.name}]` : \"\";\n stdout.write(` ${i + 1}. ${p.id}${nameTag} (${p.url})\\n`);\n });\n return 0;\n }\n\n if (parsed.command === \"proxy:remove\") {\n const id = String(parsed.args[0] || \"\").trim();\n if (!id) {\n stderr.write(\"ID proxy richiesto. Uso: llmproxy proxy:remove \\n\");\n return 1;\n }\n const removed = proxyStore.removeProxy(id);\n if (!removed) {\n stderr.write(`Proxy non trovato: ${id}\\n`);\n return 1;\n }\n stdout.write(`Proxy rimosso: ${id}\\n`);\n return 0;\n }\n\n if (parsed.command === \"proxy:reorder\") {\n const proxies = proxyStore.listProxies();\n if (proxies.length < 2) {\n stdout.write(\"Servono almeno 2 proxy per il riordino.\\n\");\n return 0;\n }\n const ids = parsed.args.filter((id) => proxies.some((p) => p.id === id));\n if (ids.length === 0) {\n stdout.write(\"Uso: llmproxy proxy:reorder ...\\n\");\n stdout.write(\"ID disponibili: \" + proxies.map((p) => p.id).join(\", \") + \"\\n\");\n return 1;\n }\n proxyStore.setProxyOrder(ids);\n stdout.write(\"Ordine proxy aggiornato.\\n\");\n return 0;\n }\n\n if (parsed.command === \"proxy:test\") {\n const proxies = proxyStore.listProxies();\n if (proxies.length === 0) {\n stdout.write(\"Nessun proxy registrato. Usa 'llmproxy proxy:add ' per aggiungere un proxy.\\n\");\n return 1;\n }\n\n stdout.write(\"Test proxy...\\n\\n\");\n let passCount = 0;\n let failCount = 0;\n\n for (let i = 0; i < proxies.length; i++) {\n const p = proxies[i];\n stdout.write(` ${i + 1}. ${p.id}... `);\n try {\n const probeFetch = makeProxyFetch(fetchFn, p.url);\n // Prova piu' host per resilienza a blocchi/rate-limit\n const testUrls = [\n \"https://api.github.com/\",\n \"https://www.google.com/\",\n \"https://cloudflare.com/cdn-cgi/trace\",\n ];\n let ok = false;\n let lastStatus = \"\";\n for (const url of testUrls) {\n try {\n const resp = await probeFetch(url, { signal: AbortSignal.timeout(10000) });\n if (resp.ok) { ok = true; break; }\n lastStatus = `HTTP ${resp.status}`;\n } catch { lastStatus = \"timeout\"; }\n }\n if (ok) {\n stdout.write(\"✅ OK\\n\");\n passCount++;\n } else {\n stdout.write(`❌ ${lastStatus}\\n`);\n failCount++;\n }\n } catch (err) {\n stdout.write(`❌ ${err.message}\\n`);\n failCount++;\n }\n }\n\n stdout.write(`\\nRisultati: ${passCount} pass, ${failCount} fail\\n`);\n return failCount === 0 ? 0 : 1;\n }\n\n if (parsed.command === \"logout\") {\n tokenStore.clear();\n stdout.write(\"Token Copilot rimosso.\\n\");\n return 0;\n }\n\n if (parsed.command === \"stop\") {\n try {\n return stopForegroundDevInstance({\n paths,\n stdout,\n stderr,\n execCommand: options.execCommand,\n killProcess: options.killProcess,\n });\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"run\") {\n const cleanupForegroundState = () => removeForegroundRunState(paths.foregroundRunPidFile);\n const { host, port } = await startServer({\n dataRoot: paths.dataRoot,\n packageRoot: paths.packageRoot,\n env,\n host: \"127.0.0.1\",\n port: 5045,\n });\n writeForegroundRunState(paths.foregroundRunPidFile, {\n pid: process.pid,\n host,\n port,\n startedAt: new Date().toISOString(),\n });\n process.once(\"exit\", cleanupForegroundState);\n process.once(\"SIGTERM\", cleanupForegroundState);\n process.once(\"SIGINT\", cleanupForegroundState);\n stdout.write(`llmProxy in ascolto su http://${host}:${port}\\n`);\n return new Promise(() => {});\n }\n\n if (parsed.command === \"status\") {\n const commandRunner = options.commandRunner || spawnSync;\n const managedStatus = getManagedServiceStatus({\n commandRunner,\n env,\n paths,\n targetPlatform,\n serviceManager,\n });\n const status = managedStatus.status;\n const providers = providerStore.listProviders();\n stdout.write(`Service manager: ${managedStatus.kind}\\n`);\n stdout.write(`Service active: ${status.active ? \"yes\" : \"no\"}\\n`);\n stdout.write(`Providers configurati: ${providers.length}\\n`);\n if (providers.length > 0) {\n stdout.write(`Active provider: ${providers[0].id}\\n`);\n stdout.write(`Fallback order: ${providers.map((provider) => provider.id).join(\", \")}\\n`);\n stdout.write(\"Providers:\\n\");\n providers.forEach((provider, index) => {\n const defaultModel = provider.default_model || (provider.provider === \"copilot\" ? DEFAULT_COPILOT_MODEL : \"\");\n const state = provider.access_token && (provider.provider === \"copilot\" || defaultModel) ? \"configured\" : \"incomplete\";\n stdout.write(`${index + 1}. ${provider.id} (${provider.name}) provider=${provider.provider || \"copilot\"} auth=${provider.auth_type || \"oauth\"} state=${state} model=${defaultModel || \"missing\"}\\n`);\n });\n }\n if (status.stdout) stdout.write(`${status.stdout}\\n`);\n if (!status.ok && status.stderr) stderr.write(`${status.stderr}\\n`);\n if (parsed.flags.docker) {\n const composeFile = String(env.LLMPROXY_DOCKER_COMPOSE_FILE || path.join(paths.packageRoot, \"docker-compose.production.yml\")).trim();\n const composeEnv = resolveServiceEnvironment({ env, paths, dockerComposeFile: composeFile });\n const spawnOptions = { env: { ...process.env, ...composeEnv } };\n stdout.write(`\\nDocker containers (${path.basename(composeFile)}):\\n`);\n const dockerCompose = resolveDockerComposeCommand(spawnSync, { spawnOptions });\n if (!dockerCompose.dockerAvailable) {\n stderr.write(\"Docker non disponibile nel PATH.\\n\");\n } else if (!dockerCompose.ok) {\n stderr.write(\"Docker Compose non disponibile (`docker compose` e `docker-compose` falliscono).\\n\");\n } else {\n const dockerResult = runDockerCompose(spawnSync, composeFile, [\"ps\", \"--format\", \"table\"], { invocation: dockerCompose, spawnOptions });\n if (dockerResult.status !== 0) {\n stderr.write(dockerResult.stderr || `Errore ${dockerCompose.label} ps.\\n`);\n } else {\n stdout.write(dockerResult.stdout || \"(nessun container trovato)\\n\");\n }\n }\n }\n return status.ok ? 0 : 1;\n }\n\n if (parsed.command === \"stats\") {\n try {\n const result = await loadMeteringStats({ env, paths, fetchFn });\n const useColor = shouldUseColorOutput(stdout, env);\n stdout.write(`${formatStatsReport(result.stats, result.source, useColor)}\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"stats:reset\") {\n try {\n const truncated = [];\n // 1) Metering records\n if (fs.existsSync(paths.meteringFile)) {\n fs.writeFileSync(paths.meteringFile, \"\", \"utf8\");\n truncated.push(\"metering\");\n }\n if (truncated.length === 0) {\n stdout.write(\"Nessun file di statistiche trovato. Nessuna azione necessaria.\\n\");\n } else {\n stdout.write(`Statistiche azzerate: ${truncated.join(\", \")}.\\n`);\n }\n return 0;\n } catch (error) {\n stderr.write(`Errore: ${error.message}\\n`);\n return 1;\n }\n }\n\n if (parsed.command === \"logs\") {\n if (parsed.follow) {\n return followLogs(paths, stdout, stderr);\n }\n const outLog = tailFile(paths.stdoutLogFile);\n const errLog = tailFile(paths.stderrLogFile);\n const requestLog = tailLatestRequestLog(paths.logsDir);\n if (outLog) stdout.write(`${outLog}\\n`);\n if (errLog) stderr.write(`${errLog}\\n`);\n if (requestLog) stdout.write(`${requestLog}\\n`);\n if (!outLog && !errLog && !requestLog) stdout.write(\"Nessun log disponibile.\\n\");\n return 0;\n }\n\n if (parsed.command === \"service:start\") {\n const result = serviceManager.install();\n if (!result.ok) {\n stderr.write((result.stderr && String(result.stderr).trim()) || \"Installazione servizio fallita.\\n\");\n return 1;\n }\n persistServiceEnvironmentConfig(paths.serviceConfigFile, serviceManagerOptions.environment);\n const commandRunner = options.commandRunner || spawnSync;\n const dockerRuntimeCheck = ensureDockerServiceRunning({\n commandRunner,\n env,\n paths,\n targetPlatform,\n });\n if (!dockerRuntimeCheck.ok) {\n stderr.write(`${dockerRuntimeCheck.error}\\n`);\n return 1;\n }\n const proxyBaseUrl = getProxyBaseUrl({ env, dataRoot: paths.dataRoot });\n const healthResult = await waitForLocalProxyHealth({\n fetchFn,\n baseUrl: proxyBaseUrl,\n sleepFn: sleep,\n });\n if (!healthResult.ok) {\n stderr.write(`Health check fallito dopo l'avvio del servizio: ${healthResult.error}\\n`);\n return 1;\n }\n if (serviceManager.kind === \"windows\" && typeof serviceManager.status === \"function\") {\n const postInstallStatus = serviceManager.status();\n if (!postInstallStatus.ok || !postInstallStatus.active) {\n stderr.write(\"Il servizio Windows e' stato registrato ma non risulta attivo dopo l'avvio.\\n\");\n return 1;\n }\n }\n stdout.write(`Servizio installato con ${serviceManager.kind}.\\n`);\n stdout.write(`stdout: ${result.stdoutPath}\\n`);\n stdout.write(`stderr: ${result.stderrPath}\\n`);\n if (dockerRuntimeCheck.checked) {\n stdout.write(`Runtime Docker: ${dockerRuntimeCheck.restarted ? \"container ricreato\" : \"container gia' attivo\"}.\\n`);\n }\n stdout.write(`Health check OK: ${proxyBaseUrl}/health\\n`);\n return 0;\n }\n\n if (parsed.command === \"service:stop\") {\n const result = serviceManager.stop();\n if (!result.ok) {\n stderr.write(result.stderr || \"Arresto servizio fallito.\\n\");\n return 1;\n }\n stdout.write(\"Servizio arrestato.\\n\");\n return 0;\n }\n\n if (parsed.command === \"service:restart\") {\n let stopResult = { ok: true, stdout: \"\", stderr: \"\" };\n let startResult;\n const commandRunner = options.commandRunner || spawnSync;\n\n if (serviceManager.kind === \"launchd\" || serviceManager.kind === \"windows\") {\n if (typeof serviceManager.install === \"function\") {\n startResult = serviceManager.install();\n } else {\n startResult = serviceManager.start();\n }\n } else {\n stopResult = serviceManager.stop();\n startResult = serviceManager.start();\n }\n\n if (!stopResult.ok || !startResult.ok) {\n stderr.write(((startResult.stderr || stopResult.stderr) && String(startResult.stderr || stopResult.stderr).trim()) || \"Riavvio servizio fallito.\\n\");\n return 1;\n }\n persistServiceEnvironmentConfig(paths.serviceConfigFile, serviceManagerOptions.environment);\n\n const dockerRuntimeCheck = ensureDockerServiceRunning({\n commandRunner,\n env,\n paths,\n targetPlatform,\n forceRecreate: true,\n });\n if (!dockerRuntimeCheck.ok) {\n stderr.write(`${dockerRuntimeCheck.error}\\n`);\n return 1;\n }\n\n const proxyBaseUrl = getProxyBaseUrl({ env, dataRoot: paths.dataRoot });\n const healthResult = await waitForLocalProxyHealth({\n fetchFn,\n baseUrl: proxyBaseUrl,\n sleepFn: sleep,\n });\n if (!healthResult.ok) {\n stderr.write(`Health check fallito dopo il restart: ${healthResult.error}\\n`);\n return 1;\n }\n if (serviceManager.kind === \"windows\" && typeof serviceManager.status === \"function\") {\n const postRestartStatus = serviceManager.status();\n if (!postRestartStatus.ok || !postRestartStatus.active) {\n stderr.write(\"Il servizio Windows e' stato riavviato ma non risulta attivo.\\n\");\n return 1;\n }\n }\n\n stdout.write(\"Servizio riavviato.\\n\");\n if (dockerRuntimeCheck.checked) {\n stdout.write(`Runtime Docker: ${dockerRuntimeCheck.restarted ? \"container ricreato\" : \"container gia' attivo\"}.\\n`);\n }\n stdout.write(`Health check OK: ${proxyBaseUrl}/health\\n`);\n return 0;\n }\n\n if (parsed.command === \"service:runtime\") {\n try {\n const targetRuntime = normalizeServiceRuntimeTarget(parsed.args[0], targetPlatform);\n if (!targetRuntime) {\n throw new Error(\"Uso: llmproxy service:runtime \");\n }\n\n const commandRunner = options.commandRunner || spawnSync;\n const runtimeProfile = normalizeRuntimeProfile(\n env.LLMPROXY_RUNTIME_PROFILE || env.LLMPROXY_ENV || env.NODE_ENV || \"production\",\n ) || \"production\";\n const targetEnv = {\n ...env,\n LLMPROXY_SERVICE_RUNTIME: targetRuntime,\n LLMPROXY_RUNTIME_PROFILE: runtimeProfile,\n LLMPROXY_ENV: runtimeProfile,\n NODE_ENV: runtimeProfile,\n };\n const targetOptions = resolveCliServiceManagerOptions({ env: targetEnv, paths, targetPlatform });\n const targetManager = targetRuntime === \"native\" && options.serviceManager\n ? options.serviceManager\n : createServiceManager(targetOptions);\n\n if (targetRuntime === \"docker\") {\n removeNativeServiceArtifacts({ targetPlatform, paths, env, managerOverride: serviceManager });\n removeDockerRuntime({ commandRunner, env: targetEnv, paths });\n persistServiceEnvironmentConfig(paths.serviceConfigFile, targetOptions.environment);\n const dockerRuntimeCheck = ensureDockerServiceRunning({\n commandRunner,\n env: targetEnv,\n paths,\n targetPlatform,\n });\n if (!dockerRuntimeCheck.ok) {\n stderr.write(`${dockerRuntimeCheck.error}\\n`);\n return 1;\n }\n } else {\n removeDockerRuntime({ commandRunner, env: targetEnv, paths });\n const result = typeof targetManager.install === \"function\"\n ? targetManager.install()\n : targetManager.start();\n if (!result.ok) {\n stderr.write((result.stderr && String(result.stderr).trim()) || \"Installazione servizio fallita.\\n\");\n return 1;\n }\n persistServiceEnvironmentConfig(paths.serviceConfigFile, targetOptions.environment);\n }\n\n const proxyBaseUrl = getProxyBaseUrl({ env: targetEnv, dataRoot: paths.dataRoot });\n const healthResult = await waitForLocalProxyHealth({\n fetchFn,\n baseUrl: proxyBaseUrl,\n sleepFn: sleep,\n });\n if (!healthResult.ok) {\n stderr.write(`Health check fallito dopo il cambio runtime: ${healthResult.error}\\n`);\n return 1;\n }\n\n stdout.write(`Runtime attivo: ${targetRuntime}\\n`);\n stdout.write(`Health check OK: ${proxyBaseUrl}/health\\n`);\n return 0;\n } catch (error) {\n stderr.write(`${error.message}\\n`);\n return 1;\n }\n }\n\n printHelp(stderr);\n return 1;\n}\n\nmodule.exports = {\n runCli,\n parseArgs,\n printHelp,\n configureClaudeSettings,\n setClaudeModel,\n formatAvailableProviders,\n formatBreakdownSection,\n formatModelList,\n formatProviderList,\n formatProviderStatus,\n formatStatsReport,\n loadMeteringStats,\n sanitizeTestAssistantText,\n getProxyBaseUrl,\n resolveDefaultModel,\n runSelfUpdate,\n runSelfUpdateWindows,\n runSelfUninstall,\n runSelfUninstallWindows,\n buildPersistentInstallScript,\n runPersistentInstall,\n resolveServiceEntryFile,\n resolveServiceEnvironment,\n resolveCliServiceManagerOptions,\n buildProxyAgentUrl,\n};\n"}}}}